Apenas para deixar claro: Não sou contra a utilização de CS e AS.
O desenvolvedor tem que lembrar que quando a base está com o collation em CS e ninguém alterou a coluna da pesquisa para CI ele vai ter resultados diferentes quando usa essa coluna em um distinct ou no where…
A algum tempo um desenvolvedor veio conversar comigo porque ele estava tendo problemas em um resultado de um select,,, coisa pouca, em uma tabela de uns 1kk de registros ele queria usar distinct para recuperar umas informações, quando ele usava o order by ele mostrava muitos resultados parecidos…
Lembrei ele que a base era CS e que ou teria que usar UPPER/LOWER ou passar um collation para a coluna da pesquisa ele ficou me olhando como se eu fosse louco…. ta certo,,, ele não estava errado,,, mas era uma solução rápida e indolor…
Exemplo:
CREATE DATABASE [BDTESTE]
COLLATE Latin1_General_100_CS_AS
GO
USE BDTESTE
GO
create table tbl_teste(
nome varchar(20)
)
insert into tbl_teste
select 'teste'
union
select 'Teste'
union
select 'tEstE
union
select 'TESTE'
SELECT *
from tbl_teste
select distinct(nome)
from tbl_teste
select distinct(UPPER(nome))
from tbl_teste
select distinct(nome) COLLATE sql_latin1_general_cp1_ci_ai
from tbl_teste
Existem outras formas de solucionar isso,,, trocar o collation da coluna direto na tabela, criar uma view, etc etc…
Os duendes de estatísticas do WordPress.com prepararam um relatório para o ano de 2012 deste blog.
Aqui está um resumo:
600 pessoas chegaram ao topo do Monte Everest em 2012. Este blog tem cerca de 10.000 visualizações em 2012. Se cada pessoa que chegou ao topo do Monte Everest visitasse este blog, levaria 17 anos para ter este tanto de visitação.
Durante o final de semana um amigo fez umas atualizações do catálogo de endereços do Exchange, 2010 adicionando informações sobre os usuários nas propriedades do AD.
Chegando hoje pela manhã ele percebeu que alguns clientes outlook não estavam com o catalogo offline atualizado. Mesmo forçando a atualização ele não baixava as modificações.
Vimos que nenhuma parte do processo estava apresentando erro para o usuário, algumas maquinas receberam as atualizações e outras não, as maquinas estavam na mesma rede e estavam com os horários corretos.
Em um teste acessei uma das maquinas e removi os arquivos .oab do perfil do usuário, após abrir o outlook ele sincronizou o catálogo offline sem problemas.
Como era apenas algumas maquinas montei um script para ajudar a resolver essa situação.
Esse script vai pedir o nome da maquina e o login da pessoa que está conectada na maquina:
$comp = read-host “Computador” $user = read-host “Usuário” cd \\$comp\c$\Users\$user\AppData dir -Recurse -Filter *.oab | remove-item
Lembrando que você tem que ter permissão administrativa suficiente para acessar a maquina remota, ele apagará todos os arquivos .oab.
Todo mundo já passou por isso,,, Depois de um passeio qualquer você e sua câmera chegam em casa e começa aquele martirio de passar as fotos da camera para o disco…
Quando você percebe tem uns poucos gigas de foto em um diretório qualquer…
Ta certo que isso não representa muito,,, tenho 2TB no NAS justamente para armazenar coisas…
Mas para você, é bem provável que fique abrindo imagem por imagem no Paint e clicando em salvar uma a uma…
Pensando em diminuir um pouco esse trabalho, montei um script, em powershell, para fazer esse trabalho chato…
Ele até que é simples, vai procurar arquivo por arquivo dentro de um diretório e em seus sub-diretórios e vai tentar salvar o arquivo com um tamanho inferior na linha 16 está configurado o tamanho máximo do novo arquivo que ele vai respeitar,,, qualquer coisa superior a isso ele vai deixar o arquivo original…
ATENÇÃO: uma coisa muito importante, eu testei esse arquivo em meu ambiente com as minhas fotos, eu sei o resultado que esperava e tenho backup. Antes de testar esse script em suas fotos faça uma cópia de algumas delas e execute o script. veja se o resultado esperado é satisfatório. Dependendo da foto ele reduz em alguns porcentos as dimensões da imagem, nada que tire a qualidade da imagem mas foi a forma do mecanismo em forçar a redução.
ATUALIZAÇÃO: Se vocês perceberam o código tem um pequeno BUG que pode atrapalhar um pouco a forma de armazenagem das fotos, na execução ele cria um novo arquivo e compara esse novo arquivo com o arquivo original, se a compressão funcionou ele remove o arquivo anterior e renomeia o novo arquivo para o nome antigo. Até aí sem problemas, o problema é que ele criou um arquivo, logo a data de criação e modificação são da data em que você executar o script. Para tentar resolver isso, adicionei duas linhas no código onde eu coloco em uma variável de data a informação com a data da última modificação (por que última modificação? porque se você mover o arquivo de lugar a data de criação é alterada) e outra linha no IF onde se o arquivo novo for menor que o arquivo original ele renomeia e troca a data de criação e modificação para a data do arquivo original.
Estou testando o script e até agora ele não falhou, caso encontrem algum bug o código está aí para ser alterado.
# Titulo: redutor de espaco ocupado por imagens jpg gif tif bmp
# Atencao: salve este arquivo com extensao .ps1 no diretorio raiz onde estao suas fotos
# abra o powershell e digite ” Set-ExecutionPolicy -ExecutionPolicy unrestricted “
# execute o arquivo que voce salvou com extensao .ps1 ele vai correr todos os arquivos no diretorio e sub-diretorio
#
# Execute este script por sua conta e risco,
# em todos os meus testes ele funcionou sem problemas mas nao quer dizer que ele vai funcionar em todos os ambientes.
#
# ricardo leka roveri
# https://leka.com.br
# @bigleka
# ricardo@leka.com.br
[reflection.assembly]::LoadWithPartialName(“System.Drawing”)
$SizeLimit=1280 # tamanho maximo que ele usa como limite
$logfile=”resizelog.txt” # Caso de erro ele gera um arquivo de relatorio
$toresize=$args[0] # lista de diretorio para localizar e modificar arquivos. pode deixar vazio
echo $toresize
$error.clear()
# primeira parte, localiza e altera arquivos jpg
Get-ChildItem -recurse $toresize -include *.jpg | foreach {
$OldBitmap = new-object System.Drawing.Bitmap $_.FullName # abre arquivo jpg
if ($error.count -ne 0) { # trata erro
$error | out-file $logfile -append -encoding default
$error[($error.count-1)].TargetObject | out-file $logfile -append -encoding default
echo $_>>$logfile
$error.clear()
}
$LongSide=$OldBitmap.Width # localiza as dimencoes
[datetime]$CreationDate=Get-ChildItem $_.FullName | Select-Object -ExpandProperty LastWriteTime # Armazena o campo da data de última modificação em uma variável para alterar a propriedade do novo arquivo
if ($OldBitmap.Width -lt $OldBitmap.Height) { $LongSide=$OldBitmap.Height }
if ($LongSide -gt $SizeLimit) { # se o arquivo localizado eh maior que o limite que voce configurou trabalha ele
if ($OldBitmap.Width -lt $OldBitmap.Height) { # calcula as dimencoes da figura
$newH=$SizeLimit
$newW=[int]($OldBitmap.Width*$SizeLimit/$OldBitmap.Height)
} else {
$newW=$SizeLimit
$newH=[int]($OldBitmap.Height*$newW/$OldBitmap.Width)
}
$NewBitmap = new-object System.Drawing.Bitmap $newW,$newH # cria o novo arquivo
$g=[System.Drawing.Graphics]::FromImage($NewBitmap)
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic # utiliza algoritimo de alta qualidade
$g.DrawImage($OldBitmap, 0, 0, $newW, $newH) # redimensiona sua imagem
$name=$_.DirectoryName+”\”+$_.name+”.new” # gera um novo nome para a imagem
$NewBitmap.Save($name, ([system.drawing.imaging.imageformat]::jpeg)) # salva a nova imagem modificada
$NewBitmap.Dispose() # limpa a bagunca
$OldBitmap.Dispose()
$n=get-childitem $name
if ($n.length -ge $_.length) { # se o arquivo novo for maior que o arquivo original
Write-host -NoNewLine “+” # escreve um “+”
$n.delete() # e apaga ele
} else { # se o arquivo novo for menor que o origial
if ($n.Exists -and $_.Exists) {
$name=$_.FullName
$_.Delete() # apaga o original
$n.MoveTo($name) # renomeia o arquivo novo com o nome do arquivo original
Get-ChildItem $n | % { $_.CreationTime = $CreationDate } # utiliza a variável da data de modificação do arquivo original para alterar o campo da data de criação do novo arquivo
echo ($Name + ” ” + $LongSide) # escreve o nome na tela para fins didaticos
}
}
} else {
Write-host -NoNewLine “.”
$OldBitmap.Dispose()
}
}
# segunda parte, arquivos que nao sao jpg
Get-ChildItem -recurse $toresize -include *.bmp, *.tif, *.gif | foreach { # neste caso arquivos bmp, tif e gif
$OldBitmap = new-object System.Drawing.Bitmap $_.FullName
if ($error.count -ne 0) {
$error | out-file $logfile -append -encoding default
$error[($error.count-1)].TargetObject | out-file $logfile -append -encoding default
$error.clear()
}
$LongSide=$OldBitmap.Width
[datetime]$CreationDate=Get-ChildItem $_.FullName | Select-Object -ExpandProperty LastWriteTime
if ($OldBitmap.Width -lt $OldBitmap.Height) { $LongSide=$OldBitmap.Height }
if ($LongSide -gt $SizeLimit) {
if ($OldBitmap.Width -lt $OldBitmap.Height) {
$newH=$SizeLimit
$newW=[int]($OldBitmap.Width*$SizeLimit/$OldBitmap.Height)
} else {
$newW=$SizeLimit
$newH=[int]($OldBitmap.Height*$newW/$OldBitmap.Width)
}
$NewBitmap = new-object System.Drawing.Bitmap $newW,$newH
$g=[System.Drawing.Graphics]::FromImage($NewBitmap)
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$g.DrawImage($OldBitmap, 0, 0, $newW, $newH)
$name=$_.DirectoryName+”\”+$_.name.substring(0, $_.name.indexof($_.extension))+”.jpg” # gera um novo nome com extensao jpg
$NewBitmap.Save($name, ([system.drawing.imaging.imageformat]::jpeg)) # salva o novo arquivo
$NewBitmap.Dispose()
$OldBitmap.Dispose()
$n=get-childitem $name
if ($n.length -ge $_.length) {
$n.delete()
Write-host -NoNewLine “+”
} else {
echo ($Name + ” ” + $LongSide)
$_.Delete()
}
} else {
$name=$_.DirectoryName+”\”+$_.name.substring(0, $_.name.indexof($_.extension))+”.jpg”
$OldBitmap.Save($name, ([system.drawing.imaging.imageformat]::jpeg))
$OldBitmap.Dispose()
$n=get-childitem $name
if ($n.length -ge $_.length) {
$n.delete()
Write-host -NoNewLine “-“
} else {
Get-ChildItem $n | % { $_.CreationTime = $CreationDate }
echo ($Name + ” ” + $LongSide)
$_.Delete()
}
}
}
Montei um script de powershell que cria uma tela para facilitar a exportação de caixas de email do exchange 2010 para PST.
Ele é bem simples,,, Antes de carregar essa tela, ele vai tentar localizar seu servidor de Exchange, vai baixar o módulo EMS para sua maquina e ai ele libera o acesso a tela acima… (o processo de baixar o EMS pode demorar um pouco)
Para localizar alguma caixa utilize o campo “Localizar” e não esqueça de colocar um * (asterisco) e depois clique em “Localizar”
Na Grid abaixo ele vai mostrar todos os resultados para sua busca, selecione a caixa que você quer exportar.
No campo salvar você pode:
Digitar o local e o nome do arquivo para ele exportar a caixa, não esqueça de colocar o nome do arquivo e o .PST
Clicar em “…” e uma tela do “Salvar como” será exibida, escolha o local para salvar o arquivo e nesse caso você não precisa colocar o .PST ele mesmo adiciona.
Clique em “Exportar”.
Detalhe muito importante:
O processo de exportação é assíncrono, isso quer dizer que: ele não ocorre durante a execução do comando de exportação, ele vai para uma fila de tratamento secundária do Exchange e depois vai ser executado. por isso ainda não conseguir colocar um status. Se você quiser saber o status da exportação da caixa pode utilizar o powershell e digitar:
O local para salvar PRECISA ser um compartilhamento da rede, pode ser endereço de loop-back, não importa, mas tem que ser endereço de rede… ainda estou trabalhando nisso
Para essa versão é isso,,, o download está no Skydrive.
Lembre-se de trocar o local onde vai salvar o arquivo do evento;
Lembre-se de se estiver usando apenas o arquivo como saída parar o evento pare uma vez ou outra a captura, dependendo da quantidade de execuções e conexões o arquivo pode crescer muito e muito rápido.
se você não configurar o filtro corretamente, você pode para seu ambiente.
A conexao do powerpivot tem que ser alterado apontando para seu servidor.
Como todo o bom DBA você tem um plano de manutenção configurado para seu ambiente SQL.
Você costuma fazer rebuild/reindex, update statistics, backup, garante que o HA está funcionado,,,
Você provavelmente deve ter a opção de “auto update statistics” habilitada em suas bases e sabe como ela funciona,,, certo?
Bom,,, no bom e velho “by the book” o “auto update statistics” funciona assim:
Em uma tabela permanente:
se a tabela não tem linhas, as estatísticas são atualizadas quando uma alteração for executada na tabela
se o número de linhas for menor que 500, as estatísticas são atualizadas a cada 500 alterações na tabela
se o número de linhas for superior a 500, as estatísticas são atualizadas a cada 20% + 500 alterações na tabela
Tabelas temporárias:
se a tabela não tem linhas, as estatísticas são atualizadas quando uma alteração for executada na tabela
se a tabela possui menos de 6 linhas, as estatísticas são atualizadas a cada 6 alterações na tabela
se o número de linhas for menor que 500, as estatísticas são atualizadas a cada 500 alterações na tabela
se o número de linhas for superior a 500, as estatísticas são atualizadas a cada 20% + 500 alterações na tabela
Tabelas variáveis (ficou feio,,, eu sei)
Não existem estatísticas em variáveis de tabela (agora ficou menos pior)
Até aí nenhuma novidade certo? certo….
Com esse conceito em mente,,, imagine que você tem umas 40 tabelas com alguns 14.000.000 de registros cada, umas outras 200 tabelas com uns 30.000 registros cada,,, você sabe quem vai ser a próxima vítima do malévolo processo de “auto update statistics” ?
Acho que muito poucas pessoas tem a opção de “auto update statistics asynchronously” habilitada, então alguma query vai sofrer com a espera da atualização de uma estatística e alguém vai achar que é lentidão no sistema…
Então como monitorar a quantidade de alterações de uma tabela pra saber se ela está chegando aos malvados 20% +500 ?
Tá lá uma query:
/*SQL 2005*/
SELECT SO.NAME AS tableName, SC.NAME AS columnName, SSC.*, SSR.* FROM sys.sysrowsetcolumns SSC
INNER JOIN sys.sysrowsets SSR ON SSC.rowsetID = SSR.rowsetID
INNER JOIN sys.sysobjects SO ON SSR.idmajor = SO.id
INNER JOIN sys.syscolumns SC on SSR.idmajor = SC.id AND SSC.rowsetcolid = SC.colid
WHERE SO.xtype = 'U'
ORDER BY so.name, sc.colid
/*SQL 2008*/
SELECT SO.NAME AS tableName, SC.NAME AS columnName, SSC.*, SSR.* FROM sys.sysrscols SSC
INNER JOIN sys.sysrowsets SSR ON SSC.rowsetID = SSR.rowsetID
INNER JOIN sys.sysobjects SO ON SSR.idmajor = SO.id
INNER JOIN sys.syscolumns SC on SSR.idmajor = SC.id AND SSC.rscolid = SC.colid
WHERE SO.xtype = 'U'
ORDER BY so.name, sc.colid
Não conseguiu executar??????? hehehehe
Precisa habilitar o DAC, as tabelas sysrscols, sysrowsetcolumns e sysrowsets só podem ser acessadas pelo DAC.
Outra coisa, não esqueça de mudar a base,,,
Mas até ai, o que tem de interessante no resultado?
O nome da tabela já sabemos, qual a coluna? grande coisa,,, o que importa é a coluna rcmodified e a coluna rcrows, quantidade de modificações e contagem de linhas respectivamente.
Agora sim,,, já começamos a ter alguma coisa legal…. mas tem como melhorar? sabemos as tabelas e as colunas… temos como saber quais as estatísticas que vão ser impactadas pela atualização? Claro…
/*SQL 2005*/
SELECT SO.NAME AS tableName, COL_NAME(sc.object_id, sc.column_id) AS columnName, A.name as stats_name, SSC.*, SSR.* FROM sys.sysrowsetcolumns SSC
INNER JOIN sys.sysrowsets SSR ON SSC.rowsetID = SSR.rowsetID
INNER JOIN sys.sysobjects SO ON SSR.idmajor = SO.id
INNER JOIN sys.stats_columns SC on SSR.idmajor = SC.object_id AND SSC.rowsetcolid = SC.column_id
INNER JOIN sys.stats as A ON A.object_id = SO.id
WHERE SO.xtype = 'U'
ORDER BY so.name, sc.column_id
/*SQL 2008*/
SELECT SO.NAME AS tableName, COL_NAME(sc.object_id, sc.column_id) AS columnName, A.name as stats_name, SSC.*, SSR.* FROM sys.sysrscols SSC
INNER JOIN sys.sysrowsets SSR ON SSC.rowsetID = SSR.rowsetID
INNER JOIN sys.sysobjects SO ON SSR.idmajor = SO.id
INNER JOIN sys.stats_columns SC on SSR.idmajor = SC.object_id AND SSC.rscolid = SC.column_id
INNER JOIN sys.stats as A ON A.object_id = SO.id
WHERE SO.xtype = 'U'
ORDER BY so.name, sc.column_id
Isso é uma informação interessante,,, por que precisamos disso? Imagine uma tabela com alguns milhões de linhas, se a atualização de estatísticas ocorre apenas a cada 20%+500 modificações é bem provável que o intervalo entre uma atualização e a outra seja um pouco grande…
Aí você pergunta: mas eu faço rebuild dos meus índices com uma boa frequência e eu sei que, com esse processo, ele já faz a atualização das estatísticas, o que eu ganho com isso?
Ai eu respondo: Você pode ter estatísticas que são criadas automaticamente,,, lembra da opção do “auto create statistics” que costuma estar habilitada por padrão? da uma olhada na sua tabela, veja se existem estatísticas começando com _WA então,,, o rebuild de índices vai atualizas as estatísticas que ele utiliza e não todas as da tabela…
A algumas semanas atrás eu estava ajudando um cliente a localizar problemas de lentidão no ambiente dele. Não havia um ponto exato, não era sempre na mesma aplicação, muitas vezes nem a mesma unidade, isso ajudava muito na localização do problema,,,
Um dos analistas de desenvolvimento me indagou se o servidor de SQL estava sofrendo com problemas de processador,,, em alguns momentos o servidor apresentava alguns picos em um dos núcleos mas nada muito longo muito menos alarmante.
Com a query abaixo, mostrei que não havia problemas de processamento,,, pelo menos naquele momento…
Select signal_wait_time_ms=sum(signal_wait_time_ms)<br>,'%signal (cpu) waits' = cast(100.0 * sum(signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2))<br>,resource_wait_time_ms=sum(wait_time_ms - signal_wait_time_ms)<br>,'%resource waits'= cast(100.0 * sum(wait_time_ms - signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2))<br>From sys.dm_os_wait_stats
O resultado é o seguinte:
Agora vem a pergunta: Legal, mas idaí?
O que nos interessa nesse resultado é o %signal (cpu) waits e o %resource waits.
A matemática é bem simples: quanto mais %recouce waits e menos %signal (cpu) waits melhor, quer dizer que, nesse momento não está havendo problemas de pressão com processador, não quer dizer que daqui a 1 segundo não comece a ter, mas no momento da execução dessa query não havia problema.
English version
A few weeks ago i was helping a client to find a slow problem on his environment. Don´t had a exact point, not always the same apllication, even the same place and this help a lot to find the problem.
One of the developers ask me if the SQL Server had problems with processos pressure,,, in a few moments on the day the server show a top processes in one of the cores, but was not the problem.
With the query below i show that we don´t have any problem with the processor, in that moment…
Select signal_wait_time_ms=sum(signal_wait_time_ms)<br>,'%signal (cpu) waits' = cast(100.0 * sum(signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2))<br>,resource_wait_time_ms=sum(wait_time_ms - signal_wait_time_ms)<br>,'%resource waits'= cast(100.0 * sum(wait_time_ms - signal_wait_time_ms) / sum (wait_time_ms) as numeric(20,2))<br>From sys.dm_os_wait_stats
The result was:
And then comes the question: Cool, but WTF?
The important is %signal (cpu) waits and %resource waits.
The mathematic is simple: more %recouce waits and lass %signal (cpu) waits is better, in that moment the server don´t have processos pressure.
Sou muito fã de ferramentas que ajudem a facilitar o dia a dia na administração do SQL, vocês já devem ter visto isso no Post Top 5 – Ferramentas Grátis,
Eu estava fazendo o download de umas ferramentas no site da RedGate e achei esse programa muito interessante,,, simples,,, gratuito,,, o SQL Index Manager,,,
Com apenas 2.4MB, ele promete uma análise de todos os índices de todas as bases que estão na sua instância e recomenda o rebuild ou reindex de cada um deles,,,
Fiz um teste em uma instância com 230 bases, com mais de 7000 índices e ele demorou aprox. 4 minutos.
A ferramenta possui uma interface extremamente simples:
Você pode deixar a ferramenta executar a manutenção para você, ou
Ela gera os scripts para que você execute manualmente
O tempo que ela demora para executar o rebuild ou reindex é o mesmo que o script ou algum plano de manutenção que você crie levaria para fazer, porque no final, a ferramenta faz a mesma coisa que você já deveria fazer…
Now in English…
I´m a fanatic about tools that help the day by day in SQL administration.
I was downloading a few tools from RedGate and find this very nice program, simple and free the SQL Index Manager,,,
With only 2.4MB, the program analyse all your indexes in all your databases and recomends a rebuild or reindex of each one.
I do a test in a server with 230 databases and more that 7000 indexes he toke 4 minutes.
The tool is extreme easy of use. (image1)
He can fix for you or generate the script. (image2)
The scripts are simples without a lot of comments (image3)
And take the same time of your Maintenance plan or custom script. (image4)
Todo mundo merece uma segunda chance,, ou para fazer melhor, ou para simplesmente não fazer,,, então,,, até no SQL temos uma segunda chance…
Dessa vez o Sr. David Howard (Blog | Twitter) deu a oportunidade de escrevermos novamente sobre algum tópico anterior do T-SQL Tuesday, ou para melhora-lo ou para fazer um se você não participou…
Eu resolvi melhorar o meu script para montar um Mirror com SQLCMD,,, na versão anterior (#25) se o banco de dados tiver mais que um datafile você tem que modificar o script na mão e compensar a criação desse outro arquivo… Nesta versão o script vai ler o arquivo de backup em uma temp table e montar o script de restore com todas as variáveis…
Vale lembrar que você precisa habilitar a opção de SQLCMD no SSMS e não esqueça de trocar o caminho do backup…
Everyone deserves a second chance, or to do better or to simply not do, then, even in SQL havea second chance…
This time Mr. David Howard (blog | twitter) gave us the opportunity to write again about a previous topic of Tuesday’s T-SQL, or to improve it or make one if you did not participate …
I decided to improve my script to create a Mirror with SQLCMD, in the previous version (#25) if the database has more than one datafile you have to modify the script and make the creation of another file … this version will read backup file in a temp table and mount the restore script with all the variables …
Remember that you need to enable the option of SQLCMD in SSMS and do not forget to change the backup path …
:SETVAR PrincipalServer (local)\INST01
:SETVAR MirrorServer (local)\INST02
:SETVAR SERVER (servername)
:SETVAR db DB_NAME
go
:ON ERROR EXIT
go
:CONNECT $(PrincipalServer)
ALTER DATABASE $(db)
SET RECOVERY FULL
go
CREATE ENDPOINT Mirroring
STATE=STARTED
AS TCP (LISTENER_PORT=5091)
FOR DATABASE_MIRRORING (ROLE=ALL)
GO
:CONNECT $(MirrorServer)
CREATE ENDPOINT Mirroring
STATE=STARTED
AS TCP (LISTENER_PORT=5092)
FOR DATABASE_MIRRORING (ROLE=ALL)
GO
:CONNECT $(PrincipalServer)
BACKUP DATABASE $(db)
TO DISK = 'D:\DB01\local\$(db).bak'
WITH INIT
GO
:CONNECT $(MirrorServer)
DECLARE @InstanceName sql_variant,
@InstanceDir sql_variant,
@SQLDataRoot nvarchar(512),
@ExecStr nvarchar(max)
SELECT @InstanceName = ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLServer')
EXECUTE master.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL',
@InstanceName, @InstanceDir OUTPUT
SELECT @ExecStr = 'EXECUTE master.dbo.xp_regread '
+ '''HKEY_LOCAL_MACHINE'', '
+ '''SOFTWARE\Microsoft\Microsoft SQL Server\'
+ convert(varchar, @InstanceDir)
+ '\Setup'', ''SQLDataRoot'', @SQLDataRoot OUTPUT'
EXEC master.dbo.sp_executesql @ExecStr
, N'@SQLDataRoot nvarchar(512) OUTPUT'
, @SQLDataRoot OUTPUT
IF @SQLDataRoot IS NULL
BEGIN
RAISERROR ('I can´t find where restore the Database... I going to stop...', 16, -1)
END
CREATE TABLE #BackupFileList
( LogicalName sysname NULL
, PhysicalName sysname NULL
, [Type] char(1)
, FileGroupName sysname NULL
, Size bigint
, MaxSize bigint
, FileId smallint
, CreateLSN numeric(25,0)
, DropLSN numeric(25,0)
, UniqueId uniqueidentifier
, ReadOnlyLSN numeric(25,0)
, ReadWriteLSN numeric(25,0)
, BackupSizeInBytes bigint
, SourceBlockSize bigint
, FileGroupId smallint
, LogGroupGUID uniqueidentifier
, DifferentialBaseLSN numeric(25,0)
, DifferentialBaseGUID uniqueidentifier
, IsReadOnly bit
, IsPresent bit
, TDEThumbPrint varbinary(32)
)
INSERT #BackupFileList
EXEC('RESTORE FILELISTONLY FROM DISK = ''D:\DB01\local\$(db).bak''')
UPDATE #BackupFileList
SET PhysicalName
= @SQLDataRoot
+ N'\Data\'
+ REVERSE(SUBSTRING(REVERSE(PhysicalName)
, 1, PATINDEX('%\%', REVERSE(PhysicalName)) -1))
DECLARE @LogicalName sysname
, @PhysicalName sysname
DECLARE FileListCursor CURSOR FAST_FORWARD FOR
SELECT LogicalName, PhysicalName
FROM #BackupFileList
OPEN FileListCursor
FETCH NEXT FROM FileListCursor INTO @LogicalName, @PhysicalName
SELECT @ExecStr = N'RESTORE DATABASE $(db)' +
N' FROM DISK = ''D:\DB01\local\$(db).bak''' +
N' WITH MOVE ''' + @LogicalName + N''' TO ''' + @PhysicalName + N''''
FETCH NEXT FROM FileListCursor INTO @LogicalName, @PhysicalName
WHILE @@FETCH_STATUS <> -1
BEGIN
SELECT @ExecStr = @ExecStr + N', MOVE ''' + @LogicalName
+ ''' TO ''' + @PhysicalName + ''''
FETCH NEXT FROM FileListCursor INTO @LogicalName, @PhysicalName
END
SELECT @ExecStr = @ExecStr + N' , NORECOVERY, REPLACE'
-- if you receive the error message because the script don´t found where he need to restore, you can uncomment the next line and get the restore command,,,
-- to restore manual...
-- SELECT @ExecStr
EXEC (@ExecStr)
DEALLOCATE FileListCursor
GO
:CONNECT $(PrincipalServer)
SELECT DATABASEPROPERTYEX(N'$(db)', N'Status') AS 'Principal Database State' -- Returns ONLINE
SELECT db_name(sd.[database_id]) AS [Database Name],
sd.mirroring_guid,
sd.mirroring_state,
sd.mirroring_state_desc,
sd.mirroring_partner_name,
sd.mirroring_witness_name,
sd.mirroring_witness_state,
sd.mirroring_witness_state_desc,
sd.mirroring_role,
sd.mirroring_role_desc,
sd.mirroring_role_sequence,
sd.mirroring_safety_level,
sd.mirroring_safety_level_desc,
sd.mirroring_safety_sequence,
sd.mirroring_failover_lsn
FROM sys.database_mirroring AS sd
WHERE sd.[database_id] = db_id(N'$(db)')
go
:CONNECT $(MirrorServer)
SELECT DATABASEPROPERTYEX(N'$(db)', N'Status') AS 'Mirror Database State' -- Returns RESTORING
SELECT db_name(sd.[database_id]) AS [Database Name],
sd.mirroring_guid,
sd.mirroring_state,
sd.mirroring_state_desc,
sd.mirroring_partner_name,
sd.mirroring_witness_name,
sd.mirroring_witness_state,
sd.mirroring_witness_state_desc,
sd.mirroring_role,
sd.mirroring_role_desc,
sd.mirroring_role_sequence,
sd.mirroring_safety_level,
sd.mirroring_safety_level_desc,
sd.mirroring_safety_sequence,
sd.mirroring_failover_lsn
FROM sys.database_mirroring AS sd
WHERE sd.[database_id] = db_id(N'$(db)')
go
:CONNECT $(PrincipalServer)
BACKUP LOG $(db)
TO DISK = 'D:\DB01\local\$(db)_Log.bak'
WITH INIT
GO
:CONNECT $(MirrorServer)
RESTORE LOG $(db)
FROM DISK = 'D:\DB01\local\$(db)_Log.bak'
WITH NORECOVERY
GO
ALTER DATABASE $(db)
SET PARTNER = 'TCP://$(server):5091'
GO
:CONNECT $(PrincipalServer)
ALTER DATABASE $(db)
SET PARTNER = 'TCP://$(server):5092'
GO
SELECT db_name(sd.[database_id]) AS [Database Name],
sd.mirroring_guid,
sd.mirroring_state,
sd.mirroring_state_desc,
sd.mirroring_partner_name,
sd.mirroring_witness_name,
sd.mirroring_witness_state,
sd.mirroring_witness_state_desc,
sd.mirroring_role,
sd.mirroring_role_desc,
sd.mirroring_role_sequence,
sd.mirroring_safety_level,
sd.mirroring_safety_level_desc,
sd.mirroring_safety_sequence,
sd.mirroring_failover_lsn
FROM sys.database_mirroring AS sd
WHERE sd.[database_id] = db_id(N'$(db)')
GO