TableDiff

Imagine a situação do post Corrompendo um Banco SQLmas na vida real,,,

Você chega um belo dia na empresa e, lê nos seus emails que, o SQL apresentou falha na estrutura de dados e algumas páginas de dados podem ter sido corrompidas,,,

Como um bom DBA, você corre para ver se o backup da noite foi feito, se os backups de transaction log também estão sendo feitos e descobre que sim,,,, todos os arquivos necessários para restaurar o banco estão lá,,,

Legal,,, mas,,, e agora? dependendo da utilização do banco você pode restaurar o backup da madrugada, os de log até o horário do problema e dali pra frente o que der pra fazer…

Em outros casos,,, você não pode se dar ao luxo de perder informação,,,,

Uma das formas seria executar o restore do banco em outro lugar, ou na mesma instância mas com outro nome, executar o checkdb e remover a página com problema e trazer a diferença dos dados,,, até aqui nada tão complicado,,, tirando o fato de se a tabela for muito grande, ou muito complexa e a query para mostrar essa diferença for muito complicado…

Para ajudar a resolver esse problema, o SQL possui uma ferramenta bem interessante chamada TableDiff.

O conceito dela é bem simples: Instância de origem, base de origem, tabela de origem, Instância de destino, base de destino, tabela de destino e o que você quer fazer…

Ex:

"C:\Program Files\Microsoft SQL Server\90\COM\tablediff.exe" -sourceserver MyServer1
                                                             -sourcedatabase MyDatabase1
                                                             -sourcetable MyTable1
                                                             -destinationserver MyServer1
                                                             -destinationdatabase MyDatabase1
                                                             -destinationtable MyTable2
                                                             -et DiffsTable

 

Nesse outro exemplo ele gera um arquivo com INSER/UPDATE/DELETE

"C:\Program Files\Microsoft SQL Server\90\COM\tablediff.exe" -sourceserver MyServer1
                                                             -sourcedatabase MyDatabase1
                                                             -sourcetable MyTable1
                                                             -destinationserver MyServer1
                                                             -destinationdatabase MyDatabase1
                                                             -destinationtable MyTable2
                                                             -et DiffsTable
                                                             -f d:\MyTable1_MyTable2_diff.sql

 

Simples certo? em teoria não é muito complicado…

Da pra deixar mais fácil? sim,,, com certeza…

O Sr. Mladen Prajdic (Blog | Twitter) desenvolveu uma interface bem interessante para ajudar na utilização do executável…

O link para download pode ser encontrado no post aqui, ou diretamente aqui.

O aplicativo é bem simples de usar, o ponto de atenção é que você precisa indicar onde está o executável do TableDiff.exe.

tablediffgui

Ele é bem auto-explicativo, pequeno e o principal,,, é de graça !!!

Vale gastar uns minutos para aprender a usar ele, vai que em um dia de emergência você precisa de uma ajuda rápida para solucionar um problema, ou ver a diferenças nas tabelas do seu logshipping….

Catalogar objetos

Esse código faz parte daquele tipo de código para gerar documentação.

Não faz nada muito complexo, apenas mostra o objeto, quem é dono, que tipo e quando foi criado.

SELECT   [NAME]          AS OBJECT_NAME,
           USER_NAME(UID)  AS OWNER,
           TYPE = CASE
                    WHEN XTYPE = 'u' THEN 'table'
                    WHEN XTYPE = 'c' THEN 'check constraint'
                    WHEN XTYPE = 'd' THEN 'default constraint'
                    WHEN XTYPE = 'f' THEN 'foreign key constraint'
                    WHEN XTYPE = 'fn' THEN 'scalar function'
                    WHEN XTYPE = 'if' THEN 'inline table function'
                    WHEN XTYPE = 'p' THEN 'stored procedure'
                    WHEN XTYPE = 'pk' THEN 'primary key'
                    WHEN XTYPE = 'tf' THEN 'table function'
                    WHEN XTYPE = 'tr' THEN 'trigger'
                    WHEN XTYPE = 'uq' THEN 'unique constraint'
                    WHEN XTYPE = 'v' THEN 'view'
                  END,
           CRDATE          AS CREATION_DATE
  FROM     SYSOBJECTS
  WHERE    XTYPE IN ('u','c','d','f',
                     'fn','if','p','pk',
                     'tf','tr','u','uq',
                     'v')
ORDER BY XTYPE

 

Gerador de Senhas

Esse é um código antigo que estava escondido no HD.

Bem simples e usual.

/*
Versao: 1.0
Data: 20100302
Por: Ricardo Leka
Site: http://leka.com.br
email: ricardo@leka.com.br
twitter: @bigleka
*/
/*
Gerador de Senhas
Pode gerar senhas com quantidades minima e maxima de caracteres
com numeros ou complexidade
*/
DECLARE
  @complex tinyint
  , @minlen tinyint
  , @maxlen tinyint  

SET @minlen = 4 --tamanho minimo da senha
SET @maxlen = 8 --tamanho maximo da senha
SET @complex = 4
-- 1 todas as letras minusculas
-- 2 inclui letras maiusculas
-- 3 inclui numeos
-- 4 inclui caracteres especiais

DECLARE
  @password varchar(12)
  , @len tinyint
  , @type  tinyint
  , @type2 tinyint

SET @len = 0
SET @password = ''
WHILE @len NOT BETWEEN @minlen and @maxlen
  BEGIN
    SET @len = ROUND(1 + (RAND(CHECKSUM(NEWID())) * @maxlen), 0) + 1
  END
WHILE @len > 0
  BEGIN
    DECLARE @newchar CHAR(1)
    SET @type = ROUND(1 + (RAND(CHECKSUM(NEWID())) * (@complex - 1)), 0)
    IF @type = 1
      SET @newchar = CHAR(ROUND(97 + (RAND(CHECKSUM(NEWID())) * 25), 0))
    IF @type = 2
      SET @newchar = CHAR(ROUND(65 + (RAND(CHECKSUM(NEWID())) * 25), 0))
    IF @type = 3
      SET @newchar = CHAR(ROUND(48 + (RAND(CHECKSUM(NEWID())) * 9), 0))
    IF @type = 4
      BEGIN
        SET @type2 = ROUND(1 + (RAND(CHECKSUM(NEWID())) * 3), 0)
        IF @type2 = 1
          SET @newchar = CHAR(ROUND(33 + (RAND(CHECKSUM(NEWID())) * 14), 0))
        IF @type2 = 2
          SET @newchar = CHAR(ROUND(58 + (RAND(CHECKSUM(NEWID())) * 6), 0))
        IF @type2 = 3
          SET @newchar = CHAR(ROUND(91 + (RAND(CHECKSUM(NEWID())) * 5), 0))
        IF @type2 = 4
          SET @newchar = CHAR(ROUND(123 + (RAND(CHECKSUM(NEWID())) * 3), 0))
      END
-- remove caracteres que podem ser confundidos com outros
    IF @newchar NOT IN ('b', 'l', 'o', 's', 'I', 'O', 'S', '0', '1', '!', '''', '.', ',', '/', '`', '\', '|')
      BEGIN
        SET @password = @password + @newchar
        SET @len = @len - 1
      END
  END
SELECT @password as Senha

T-SQL Tuesday #21 – Depois arrumo esse código,,,

Dessa vez o anfitrião do T-SQL Tuesday é o próprio idealizador Adam Machanic (Blog | Twitter).

E de uma forma diferente, não é na Terça-feira,,, por quê?

Porque não importa, desde que funcione pode ser zuado mesmo,,, o tópico desse mês é sobre “mostrar seu código lixo para o mundo”,,,

Todo mundo já escreveu um código uma vez ou outra,,,, todo o DBA tem aquele conjunto de scripts que o ajudam a identificar alguns problemas, fazer um tunning, arrumar alguma coisa,,, E com certeza todo mundo tem aquele código que hoje, pega para olha e começa a dar risada de como é que teve coragem de fazer uma coisa como aquela…

Meu exemplo é bem simples, O código é horrível, demora demais mas, funciona,,,

Ele troca os dados de posição de uma coluna específica.

WITH cteTableTel AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY NEWID()) AS n,
        SOBRENOME
    FROM CLIENTES
    )
UPDATE CLIENTES
   SET ENDERECO = (
       SELECT ENDERECO
       FROM cteTableTel
       WHERE cteTableTel.n = CLIENTES.ID)
Esse código funciona, não é nada bonito, não é performático, mas para a necessidade de uma base de treino com dados reais ele ajuda bastante,,,
Se você percebeu ele tem um problema,,, sabe qual é?
Como você garante que todas as linhas da tabela de clientes realmente estão em uma sequencia? Você pode ter apagado algum registro uma hora ou outra,,, Logo, haverá cliente que o endereço não vai ser atualizado,,
Já sei, você nem reparou nesse problema,,, você deve estar pensando: “por que ele esta fazendo um update na tabela de clientes se ele está usando uma CTE?”, ou também, “Legal, ele está fazendo isso em uma tabela que tem algum tipo de ID e quando tem aquelas tabelas que não da pra usar nada de referencia?”
Como eu disse, ele não é performático, e eu nem tinha me atentado nesses detalhes na época,,,  Houveram duas situações que me refizeram rever esse código: o primeiro caso dos usuários que não tinham os dados alteradores e o último… ai com muita vontade resolvi reescrever,,,
E ele ficou mais ou menos assim:
;WITH cte AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY NEWID()) AS n1,
        ROW_NUMBER() OVER (order by SOBRENOME) AS n2,
        SOBRENOME
    FROM CLIENTES
    )
UPDATE c1
   SET SOBRENOME = c2.SOBRENOME
   from cte as c1
   inner join cte as c2
   on c1.n1 = c2.n2

Dessa vez não tem problema com o usuário sem o dado alterado, performance melhorada em quase 90% e pouco importa se a tabela em algum registro para referencia,,,

Eu comparei a execução dos 2 códigos em uma tabela com um pouco mais de  3 milhões de linhas e o segundo código demorou quase 4 horas para finalizar,,, já o primeiro código eu parei a execução dele depois de 2 SEMANAS executando.

Tenho códigos piores no meu repositório? claro que sim. Esse é apenas um exemplo de um dos piores que já fiz…

Qual é o seu? tem coragem mostrar?

T-SQL Tuesday #20 – Melhores Práticas

Neste mês o tema escolhido pelo Sr. Amit Banerjee (blog | twitter) foi: Melhores Práticas,,,

A algum tempo atrás escrevi um post com “Dicas para otimizar suas funções SQL”,,, vou adicionar algumas coisas que acho interessante com este post aqui.

  • Comentários,,,,

Lembra daquela procedure que fazia uma validação de alguma coisa em algum lugar ??? Lembra por que você declarou aquele campo bit no começo ??? Não? tem certeza que sabe? acha que era para alguma coisa importante?

Uma coisa que acho muito importante em todo o código, mas são poucas as pessoas que fazem direito e menos ainda as que fazem, é comentar o que ele faz,,,

Não custa muito adicionar algumas linhas com algum tipo de descritivo do que aquilo deveria fazer, quem fez, quando fez, qual a versão, algum exemplo,,,

Lembre-se: Uma hora ou outra o código pode precisar de manutenção,,, ela pode ser feita por você ou outra pessoa,,, em todo o caso,,, é sempre bom ter alguma coisa para te ajudar a lembrar,,,

Ex:

/*
Nome: usp_mostra_nome_base
Versão: 2.0
Data de criação: 11-07-2011
Data da última modificação: 12-07-2011

Autor: Ricardo Leka Roveri
Últuma modificação executada por: Ricardo Leka Roveri

Script:
Esta procedure server para listas o nome das bases existentes no sistema.

Exemplo
exec usp_mostra_nome_base

Versão 1:
Autor: Ricardo Leka Roveri

nesta versão a proc faz XYZ

Versão 2:
Autor: Ricardo Leka Roveri

nesta versão a proc faz XYZ ordenado por data de criação

*/

Outra coisa interessante seria comentar partes do código,,, não precisa escrever um livro do motivo que você está fazendo aquele join entre 20 tabelas mas, de uma forma clara, escrever o objetivo dessa bagunça,,,

  • NOLOCK,,,

Você é um fanático por NOLOCK? Legal,,, nada contra,,, mas se vai usar isso umas 40 vezes dentro de uma procedure porque você não declara ele como:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;

é legal, ajuda, mais simples pra você e para qualquer outra pessoa que veja seu código…

  • Lembrete,,,

Lembre-se:

A maior parte de vocês não desenvolve coisas pra vocês,,, vocês desenvolvem coisas pra outras pessoas usarem…

Não é porque na sua maquina tudo funciona direitinho que quando for para produção tem que funcionar,,, Você precisa testar,,, faça teste de carga… stresse sua aplicação até ela parar,,, 10,,, 100,,, 1000,,, acessos simultâneos,,,

Existem muitas ferramentas que fazem teste de carga. Ex.: JMeter, o próprio SQL Profiler, etc..

T-SQL Tuesday #19 – Disasters & Recovery

E ai? Blz? Neste mês para o T-SQL Tuesday o tema escolhido pelo Sr. (blog | twitter) foi Desastre & Recover…. Que tema chato,,, hehehe,,,

Muitas pessoas – administradores de rede, gerentes, desenvolvedores, dba´s acidentais, etc. – acham que banco de dados é a coisa mais simples do mundo, só serve pra guardar alguma informação e que aquilo vai fica sempre por lá. Esse pessoal não entende que se tem gente que é especialista em administrar banco de dados é porque por algum motivo obscuro isso é importante.

Muitos não dão valor ao trabalho do Administrador de banco até perder alguma coisa, uma boa galera acha que para manter um banco qualquer pessoa consegue, afinal, não tem muito trabalho pra fazer,,,

Ainda bem que existe esse tipo de gente,,, é ainda mais legal quando alguém me liga no meio da madrugada com aquela voz desesperada pedindo ajuda porque o servidor do banco (que era o mesmo do AD, Exchange, FileServer, DHCP) foi pro espaço e eles não estão conseguindo mais recuperar as coisas…

Muitas vezes vejo o pessoal falando sobre seus planos de backup, que contemplam X ou Y e blá blá blá,,, Mas quando pergunto qual o seu plano de recovery o pessoal olha torto e me mostra o plano de backup…

Pessoal,,,, entendam uma coisa,,,, plano de backup é bonito pra não passar feio na frente da diretoria,,, mas ter plano de recovery é o que realmente importa…

Uma coisa que pergunto pro pessoal é: “O quanto de dados você está disposto a perder?”, claro que quase sempre tenho a mesma resposta, ninguém quer perder nada,,,

Hoje em dia existem várias formas de se prevenir quando a perda de informação:

  • Backup
  • Cluster
  • Mirror
  • Log shipping
  • Replicação
  • Etc.

Claro que cada uma delas tem seus custos,,,

Manter os dados acessíveis é importante isso é disponibilidade,,, mas,,, e quando a casa caiu? Alguém aplicou alguma mudança no ambiente e apagou um monte de registro que não deveria? O storage deu rebuild da LUN e apagou todos os seus discos,,, o Windows deu tela azul…

Iai? É nessa hora que você vai descobrir que seu robô de backup não consegue ler as fitas,,, ou que o software de backup não fazia backup justamente daquela unidade ou base que você precisa… ou mais legal, quase todo o dia você cancelava a rotina de backup porque ela entrava no horário de produção, ai não fazia backup das principais bases…

Neste caso,,, parabéns !!! troca de cidade, apaga essa empresa do currículo, sai correndo,,,

Plano de desastre & recovery é igual a seguro de carro,,, você faz pra não usar,,, mas quando precisa e ele falha o que faz? Chora? Troca?

A pior coisa que pode acontecer em um momento de crise é o pânico. Mantenha a calma, com certeza vão ter pessoas desesperadas correndo que nem baratas tontas tentando qualquer coisa que lembrar ou encontrar na internet.

Lembre-se: você tem que ser assertivo, se você fez a lição de casa não existe situação que não pode ser contornada.

Seu supervisor/gerente/diretor tem que ter maturidade de segurar todas as buchas enquanto você se foca em colocar em ação o plano de desastre.

Uma dica, noticia ruim se dá na hora. Perdeu? Perdeu,,, não da pra recuperar? Já era? Avisa logo…

A minha versão do Blitz

A algum tempo atras assisti uma apresentação web do Sr. Brent Ozar (Blog | Twitter), onde ele demonstrava um conjunto de scripts para um “levantamento sem intervenção” de um servidor SQL Server.

Achei a ideia muito interessante,,, Como em muitas vezes apenas chegamos em uma empresa e já somos apresentados a um servidor com SQL é interessante saber o que acontece, ou o que ele tem, antes de começar a fazer qualquer coisa. Em outros casos, acontece também de alguém chegar pra você e perguntar alguma coisa de algum servidor que você nunca ouviu falar,,, Sempre vem acompanhado daquela história triste do consultor que veio fazer a instalação disse que precisava de um banco, ai alguém subiu uma maquina (quando não coloca na mesma maquina) e que agora está dando problema e eles não dão suporte,,,, ai sobra pra quem???? adivinha ?!?!?!?

Eu fiz algumas alterações no que achei mais interessante, mas o script original você pode achar no site do Brent Ozar.

/*
Versao: 0.5
Data:    20110523
Por:    Ricardo Leka
Site:   
http://leka.com.br
email:    ricardo@leka.com.br
*/
/*
Versao do SQL
Traz informacoes interessante sobre o nome da maquina, se tem instancia, versao de produto,
SP, collation da instancia, se esta ou nao em cluster
*/

Print ‘Versao de SQL’

SELECT
CAST( SERVERPROPERTY( ‘MachineName’ ) AS varchar( 30 ) ) AS MachineName ,
CAST( SERVERPROPERTY( ‘InstanceName’ ) AS varchar( 30 ) ) AS Instance ,
CAST( SERVERPROPERTY( ‘ProductVersion’ ) AS varchar( 30 ) ) AS ProductVersion ,
CAST( SERVERPROPERTY( ‘ProductLevel’ ) AS varchar( 30 ) ) AS ProductLevel ,
CAST( SERVERPROPERTY( ‘Edition’ ) AS varchar( 30 ) ) AS Edition ,
( CASE SERVERPROPERTY( ‘EngineEdition’)
WHEN 1 THEN ‘Personal or Desktop’
WHEN 2 THEN ‘Standard’
WHEN 3 THEN ‘Enterprise’
END ) AS EngineType ,
CAST( SERVERPROPERTY( ‘LicenseType’ ) AS varchar( 30 ) ) AS LicenseType ,
SERVERPROPERTY( ‘NumLicenses’ ) AS #Licenses,
sERVERPROPERTY(‘Collation’) AS Collation,
( CASE SERVERPROPERTY( ‘IsClustered’)
WHEN 0 THEN ‘Nao’
WHEN 1 THEN ‘Sim’
END ) AS Cluster;

SELECT @@VERSION AS [SQL Server Details];

/*
Pega algumas informacoes interessantes como
nome, owner, data de criação, dbid, Modo de Compatiblidade das bases, recovery model,  versao e status

ATENCAO: Lembre-se que a ideia do script eh de apenas ver o que tem,
caso encontre alguma base com status diferente de online ele nao faz nada
e na teoria ainda nao eh a hora de voce fazer. Apenas reporte.
*/

Print ‘Nome, owner, dbcreate, dbid, cmptlvl, recovery, version, status’

select    name as [NAME]
        ,suser_sname(sid) as [Owner]
        ,convert(nvarchar(11), crdate) as [Data Criacao]
        ,dbid as [DBID]
        ,cmptlevel
        ,databasepropertyex(name, ‘recovery’)
        ,version
        ,databasepropertyex(name, ‘status’)
from master.dbo.sysdatabases

/*
Backup FULL

Uma das informacoes mais importantes, tem backup?
*/

Print ‘Backup FULL’

SELECT d.name, MAX(b.backup_finish_date) AS last_backup_finish_date
FROM master.sys.databases d
LEFT OUTER JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = ‘D’
WHERE d.database_id NOT IN (2, 3)
GROUP BY d.name
ORDER BY 2 DESC

/*
Backup LOG

Outra informacao muito importante,,,,
*/

Print ‘Backup Log’

SELECT d.name, d.recovery_model, d.recovery_model_desc
FROM master.sys.databases d
LEFT OUTER JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = ‘L’
WHERE d.recovery_model IN (1, 2) AND b.type IS NULL AND d.database_id NOT IN (2, 3)

/*
Localizacao dos backups

Se houve backup, pra onde ele foi?
*/

Print ‘Localizacao dos Backups’

SELECT TOP 100 physical_drive, physical_name, *
FROM msdb.dbo.backupfile
ORDER BY 1 DESC

/*
A quanto tempo existe historico de backup?

No SQL 2k, havia um grande problema de performance quando o historico ficava muito grande.
*/

Print ‘Backup History’

SELECT TOP 1 backup_start_date
FROM msdb.dbo.backupset WITH (NOLOCK)
ORDER BY backup_set_id ASC

/*
Quando foi que o DBCC CHECKDB rodou pela última vez?

Isso eh tao bom quando retorna alguma coisa,,,
*/

Print ‘DBCC CHECKDB’

CREATE TABLE #temp (         
       ParentObject     VARCHAR(255)
       , [Object]       VARCHAR(255)
       , Field          VARCHAR(255)
       , [Value]        VARCHAR(255)  
   )  
  
CREATE TABLE #DBCCResults (
        ServerName           VARCHAR(255)
        , DBName             VARCHAR(255)
        , LastCleanDBCCDate  DATETIME  
    )  
   
EXEC master.dbo.sp_MSforeachdb      
           @command1 = ‘USE ? INSERT INTO #temp EXECUTE (”DBCC DBINFO WITH TABLERESULTS”)’
           , @command2 = ‘INSERT INTO #DBCCResults SELECT @@SERVERNAME, ”?”, Value FROM #temp WHERE Field = ”dbi_dbccLastKnownGood”’
           , @command3 = ‘TRUNCATE TABLE #temp’  
  
   –Delete duplicates due to a bug in SQL Server 2008
  
   ;WITH DBCC_CTE AS
   (
       SELECT ROW_NUMBER() OVER (PARTITION BY ServerName, DBName, LastCleanDBCCDate ORDER BY LastCleanDBCCDate) RowID
       FROM #DBCCResults
   )
   DELETE FROM DBCC_CTE WHERE RowID > 1;
  
    SELECT       
           ServerName      
           , DBName      
           , CASE LastCleanDBCCDate
                   WHEN ’1900-01-01 00:00:00.000′ THEN ‘Never ran DBCC CHECKDB’
                   ELSE CAST(LastCleanDBCCDate AS VARCHAR) END AS LastCleanDBCCDate   
   FROM #DBCCResults  
   ORDER BY 3
  
   DROP TABLE #temp, #DBCCResults;

/*
Quem faz parte do sysadmin ou securityadmin

Quantas outras pessoas podem acessar o SQL e fazer algum tipo de estrago?
*/

SELECT    l.name
        ,l.denylogin
        ,l.isntname
        ,l.isntgroup
        ,l.isntuser
FROM master.dbo.syslogins l
WHERE l.sysadmin = 1 OR l.securityadmin = 1
ORDER BY l.isntgroup, l.isntname, l.isntuser

/*
Configurações da instância

Isso mostra como esta configurado hoje, e nao como estava configurado…
*/
EXEC dbo.sp_configure ‘show advanced options’, 1
GO
RECONFIGURE
GO
EXEC dbo.sp_configure

/*
Email

O SQL envia email?
*/

Print ‘Email’

EXEC msdb.dbo.sp_send_dbmail
    @recipients = ‘ricardo@leka.com.br’,
    @body = @@SERVERNAME,
    @subject = ‘Testando SQL Server Database Mail – veja no corpo o nome do servidor’;
GO

/*
Configurando o sistema de email para enviar mensagem de erros

Aqui vai alguma configuracao,,, nada critica,,,
*/

Print ‘Configurando o sistema de emails’

USE [msdb]
GO
EXEC msdb.dbo.sp_add_operator @name=N’The Database Administrator’,
        @enabled=1,
        @weekday_pager_start_time=0,
        @weekday_pager_end_time=235959,
        @saturday_pager_start_time=0,
        @saturday_pager_end_time=235959,
        @sunday_pager_start_time=0,
        @sunday_pager_end_time=235959,
        @pager_days=127,
        @email_address=N’suporte@asd.com’,
        @pager_address=N’0119999999@operadora.com.br’,
        @category_name=N’[Uncategorized]‘
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 016′,
        @message_id=0,
        @severity=16,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 016′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 017′,
        @message_id=0,
        @severity=17,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 017′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 018′,
        @message_id=0,
        @severity=18,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 018′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 019′,
        @message_id=0,
        @severity=19,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 019′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 020′,
        @message_id=0,
        @severity=20,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 020′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 021′,
        @message_id=0,
        @severity=21,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 021′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 022′,
        @message_id=0,
        @severity=22,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 022′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 023′,
        @message_id=0,
        @severity=23,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 023′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 024′,
        @message_id=0,
        @severity=24,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 024′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO
EXEC msdb.dbo.sp_add_alert @name=N’Severity 025′,
        @message_id=0,
        @severity=25,
        @enabled=1,
        @delay_between_responses=60,
        @include_event_description_in=1,
        @job_id=N’00000000-0000-0000-0000-000000000000′
GO
EXEC msdb.dbo.sp_add_notification @alert_name=N’Severity 025′, @operator_name=N’The Database Administrator’, @notification_method = 7
GO

/*
Objetos em bases de sistema

Coisas que nao deveriam estar neste lugar
*/

Print ‘Coisas no lugar errado’

SELECT * FROM master.sys.tables WHERE name NOT IN (‘spt_fallback_db’, ‘spt_fallback_dev’, ‘spt_fallback_usg’, ‘spt_monitor’, ‘spt_values’, ‘MSreplication_options’)
SELECT * FROM master.sys.procedures WHERE name NOT IN (‘sp_MSrepl_startup’, ‘sp_MScleanupmergepublisher’)
SELECT * FROM model.sys.tables
SELECT * FROM model.sys.procedures

/*
Start up Stored procedure

Achou alguma procedure aqui? estranho,,, veja o que ela faz,,,
pode ser o mau esperando um boot,,,
*/

SELECT *
FROM master.INFORMATION_SCHEMA.ROUTINES
WHERE OBJECTPROPERTY(OBJECT_ID(ROUTINE_NAME),’ExecIsStartup’) = 1

/*
Quem mais existe em server-level
*/

SELECT * FROM sysservers

/*
Agora mais informacoes sobre as bases

classico,,, nada fora do comum,,,
*/

SELECT * FROM sysdatabases

/*
Onde estao os arquivos

onde eles estao? quantos sao?
*/

CREATE TABLE #DatabaseFiles(
    [database_name] [sysname] NOT NULL,
    [file_id] [int] NOT NULL,
    [file_guid] [smallint] NULL,
    [size] [int] NOT NULL,
    [max_size] [int] NOT NULL,
    [growth] [int] NOT NULL,
    [status] [int] NULL,
    [perf] [int] NOT NULL,
    [name] [sysname] NOT NULL,
    [physical_name] [nvarchar](260) NOT NULL,
)
EXEC dbo.sp_MSforeachdb ‘INSERT INTO #DatabaseFiles SELECT ”[?]” AS database_name, * FROM [?].dbo.sysfiles’
SELECT * FROM #DatabaseFiles ORDER BY database_name
DROP TABLE #DatabaseFiles

/*
Triggers

isso pode demorar um pouco,,,
e mais ainda pra ler,,,,
*/

Print ‘Triggers’

EXEC dbo.sp_MSforeachdb ‘SELECT ”[?]” AS database_name, o.name AS table_name, t.* FROM [?].sys.triggers t INNER JOIN [?].sys.objects o ON t.parent_id = o.object_id’

/*
Alguns Waits

Nao zere o contador, apenas veja o que tem,,,
*/

Print ‘Alguns Waits’

SELECT *, (wait_time_ms – signal_wait_time_ms) AS real_wait_time_ms
FROM sys.dm_os_wait_stats
ORDER BY (wait_time_ms – signal_wait_time_ms) DESC

/*
Frangmentacao de indices

se voce achou que o de Triggers demorou,,, imagina esse,,,,
*/

Print ‘Fragmentacao de indices’

SELECT
      db.name AS databaseName
    , ps.OBJECT_ID AS objectID
    , ps.index_id AS indexID
    , ps.partition_number AS partitionNumber
    , ps.avg_fragmentation_in_percent AS fragmentation
    , ps.page_count
FROM sys.databases db
  INNER JOIN sys.dm_db_index_physical_stats (NULL, NULL, NULL , NULL, N’Limited’) ps
      ON db.database_id = ps.database_id
WHERE ps.index_id > 0
   AND ps.page_count > 100
   AND ps.avg_fragmentation_in_percent > 30
OPTION (MaxDop 1);

/*
Jobs

Simples,,, quais sao, a quem pertence e se estao ativos,,,
NAO tente corrigir-los agora,,, entenda o que eles fazem ou deveriam fazer,,,
*/

Print ‘Jobs’

select     Name as [Name]
    ,SUSER_SNAME(owner_sid) as [Owner]
    ,(    Case enabled
        WHEN 0 THEN ‘Nao’
        WHEN 1 THEN ‘Sim’
        END
    ) AS [Enable]
    , description as [Description]
from msdb.dbo.sysjobs_view
order by name

/*
Users

Quais sao os usuarios de Windows e os de SQL
Preste atencao quais sao os usuarios, podem tem mais do que precisa,,,,
*/

Print ‘Usuarios windows e SQL’

select     Name as [Name]
    ,(    Case isntname
        WHEN 0 THEN ‘SQL Server Standard’
        WHEN 1 THEN ‘Windows Authentication’
        END
    ) AS [Type]
from master.sys.syslogins

/*
Usuarios x Bases

Esse aqui nao deve demorar muito,,,
apenas gera um relacionamento dos usuarios que estao nas bases
compare com a quantidade de usuarios que voce pegou na lista acima
*/

Print ‘Usuarios por Bases’

create table #UsrDataMapping(
[database_name] [sysname] NULL,
[name] [sysname] NULL,
[schema] [sysname] NULL
)
EXEC sp_MSforeachdb ‘insert into #UsrDataMapping SELECT ”?” as DBNAME,
–u.name AS [Name],
SUSER_SNAME(sid) AS [Name],
ISNULL(u.default_schema_name,N””) AS [DefaultSchema]
FROM
[?].sys.database_principals AS u
LEFT OUTER JOIN [?].sys.database_permissions AS dp ON dp.grantee_principal_id = u.principal_id
WHERE
(u.type in (”U”, ”S”, ”G”, ”C”, ”K”))
and dp.state = ”G”

select * from #UsrDataMapping order by database_name
drop table #UsrDataMapping

(update 28/09/2011) ATUALIZAÇÃO !!!

Pessoal, desculpe ter colocado o código sem uma formatação de cores, mas o plug-in do Microsoft Live Write é muito ruim,,, e ele quebra todo o código…

O arquivo com o script está no SkyDrive.

Quando foi que trocaram a senha?

Quando estamos no SQL Server (2005 ou superior), existem uma função que mostra algumas propriedades interessante sobre o login do SQL chamada LoginProperty.

Não conheço muitas aplicações que possuem interface que permite o usuário trocar a senha dele no SQL,,, mas achei interessante ter essa opção para saber quando alguém trocou a senha e “esqueceu” de avisar,,, ai tem aplicação que não abre,,, usuário que não loga,,, e quase sempre ninguém nunca fez nada….

Aqui vão alguns selects interessantes….

Mostra todos os logins que tiveram a senha trocada a mais de 30 dias:

 1: SELECT name, LOGINPROPERTY([name], 'PasswordLastSetTime') AS 'SenhaTrocada'
 2: FROM sys.sql_logins
 3: WHERE LOGINPROPERTY([name], 'PasswordLastSetTime') < DATEADD(dd, -60, GETDATE());

Mostra todos os logins que tiveram a senha trocada no último dia:

 1: SELECT name, LOGINPROPERTY([name], 'PasswordLastSetTime') AS 'SenhaTrocada'
 2: FROM sys.sql_logins
 3: WHERE LOGINPROPERTY([name], 'PasswordLastSetTime') > DATEADD(dd, -1, GETDATE());

T-SQL Tuesday #18 – CTEs

Bom,,, para este mês o Sr. Bob Pusateri (twitter | Blog) escolheu o tópico Common Table Expressions (CTE).

Você não sabe o que é? Não faz ideia? Isso é mais comum do que você imagina,,, vejo muitos códigos por ai onde o pessoal de dev poderia utilizar este recurso, mas como ainda estão presos em conceitos antigos do SQL 2000 ou as vezes até mais velhos não fazem ideia de alguns novos recursos interessantes…

Para uma leitura interessante sobre este assunto acesse:

Você vai achar muita coisa voltada pra dev… mas,,, como sou mais um cada de infra,,, achei interessante este código no Site do Sr. Paul Randal (twitter | Blog) sobre wait statistics usando a sys.dm_os_wait_stats, nada muito complexo.

 1: WITH Waits AS
 2:     (SELECT
 3:         wait_type,
 4:         wait_time_ms / 1000.0 AS WaitS,
 5:         (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
 6:         signal_wait_time_ms / 1000.0 AS SignalS,
 7:         waiting_tasks_count AS WaitCount,
 8:         100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
 9:         ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
 10:     FROM sys.dm_os_wait_stats
 11:     WHERE wait_type NOT IN (
 12:         'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
 13:         'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
 14:         'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
 15:         'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_SEMAPHORE',
 16:         'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
 17:         'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
 18:         'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
 19:         'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
 20:         'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
 21:     )
 22: SELECT
 23:     W1.wait_type AS WaitType,
 24:     CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
 25:     CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
 26:     CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
 27:     W1.WaitCount AS WaitCount,
 28:     CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
 29:     CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
 30:     CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
 31:     CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
 32: FROM Waits AS W1
 33:     INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
 34: GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount, W1.Percentage
 35: HAVING SUM (W2.Percentage) - W1.Percentage < 95;
 36: GO

T-SQL Tuesday #17–Apply

O tópico deste mês para o T-SQL Tuesday é o operador apply,,,

Caso você não faça ideia de pra que serve o apply leia aqui.

Para este post vou coloca um script simples usando o apply.

Ele não faz nada muito importante, apenas mostra as 20 querys que mais gerão stress de disco:

SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END – qs.statement_start_offset)/2)+1),
qs.execution_count,
qs.total_logical_reads, qs.last_logical_reads,
qs.min_logical_reads, qs.max_logical_reads,
qs.total_elapsed_time, qs.last_elapsed_time,
qs.min_elapsed_time, qs.max_elapsed_time,
qs.last_execution_time,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qt.encrypted=0
ORDER BY qs.total_logical_reads DESC