quinta-feira, 27 de agosto de 2009

Combinação de valores Base-2.


Combinação de valores Base 2 é bastante utilizado em "campos" onde precisamos guardar uma combinação de "possibilidades" ou opções em um único campo. Por exemplo em campos de status ou pendencias.


O valor resultante desta combinação único, nenhuma outra combinação resulta no mesmo valor, então com uma lógica simples é possivel achar essa combinação.


Um exemplo disso são os campos Status e Status2 da tabela sys.databases ( http://msdn.microsoft.com/pt-br/library/ms179900.aspx )


Select Name, Status, Status2 from Master.dbo.sysdatabases


Name Status Status2
---------- ----------- -----------
master 65544 1090520064


O Status 65544 é a combinação de 65536 e 8
O Status2 1090520064 é a combinação de 1073741824, 16777216 e 1024

De qualquer forma, a sequencia é sempre número anterior * 2, ou em base-2: 2^n.

2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, etc, etc, etc.....

Seguindo o exemplo de "pendências", para facilitar vamos imagina um caso onde um cliente fez um cadastro em uma loja para contratar um financiamento, e está "devendo" alguns documentos para terminar o processo do financiamento:

Código Valor Descrição
1 2 Falta RG
2 4 Falta CPF
3 8 Falta Certidão de Nascimento
4 16 Falta Certidão de Casamento
5 32 Falta Certidão Negativa
6 64 Falta Comprovante de Renda
7 128 Falta Comprovante de Residencia
8 256 Falta Reservista
9 512 Falta Passaporte
10 1024 Falta Comprovante de Votação

Combinando os valores 3 (8), 5 (32), 7 (128), 8 (256) e 10 (1024) = 1448
Ainda faltam para o cliente:
- Falta Certidão de Nascimento
- Falta Certidão Negativa
- Falta Comprovante de Residencia
- Falta Reservista
- Falta Comprovante de Votação

Uma forma simples de se fazer isso é, sabendo que a soma dos números anteriores nunca é maior ou igual à atual, fazer um loop decrescente "easy cake" diminuindo do resultado final +/- assim:


/************************************************************************************************/
Declare @Numero float, @LimiteSuperior float, @iLimiteSuperior Int

Set @Numero = 1448

Set @LimiteSuperior = 562949953421312 -- 49 opções se precisar mais que isso é só calcular.

Set @iLimiteSuperior = 49

Declare @Resultados table (Base int, Valor float)
While @Numero > 0
Begin
If @LimiteSuperior < = @Numero Begin

Insert Into @Resultados Values (@iLimiteSuperior, @LimiteSuperior)
Set @Numero = @Numero - @LimiteSuperior

End

Set @LimiteSuperior = @LimiteSuperior / 2 --Acha o valor anterior
Set @iLimiteSuperior = @iLimiteSuperior - 1

End

Select * from @Resultados

/************************************************************************************************/

Outra forma, que eu acho mais interessante, tambem é um loop decrescente, porem trabalhamos com Log Base-2 que conseguimos com a divisão: LOG(Numero)/LOG(2).

/************************************************************************************************/
Declare @Numero float, @Expoente float
Declare @Resultados table (Base int, Valor float)

Set @Numero = 1448

While @Numero > 0
Begin

Set @Expoente = FLOOR(LOG(@Numero)/LOG(2))
Insert Into @Resultados Values (@Expoente, POWER(cast(2 as float), @Expoente))
set @Numero = @Numero - POWER(cast(2 as float), @Expoente)

End

Select * from @Resultados

Gerar Senhas Randomicas parte 2.

Esse código eu utilizava em uma Function para gerar senhas randomicas em algumas ocasiões no sistema.

Como parametro de entrada da function eu passava os itens abaixo e recebia lista de senhas como Table.

@GrupoBitMinuscula
@GrupoBitNumericos
@GrupoBitEspeciais
@GrupoBitDiferenci
@GrupoCharDiferenci
@QuantidadeSenha
@TamanhoSenha


Declare @GrupoCharMaiuscula VarChar(26),
@GrupoCharMinuscula VarChar(26),
@GrupoCharNumericos VarChar(10),
@GrupoCharEspeciais VarChar(26),
@GrupoCharDiferenci VarChar(Max)


Declare @GrupoBitMaiuscula Bit,
@GrupoBitMinuscula Bit,
@GrupoBitNumericos Bit,
@GrupoBitEspeciais Bit,
@GrupoBitDiferenci Bit

Declare @GrupoCharTotal VarChar(Max),

Declare @TextoSenha Table (dsSenha VarChar(Max))

Declare @tmpTextoSenha VarChar(Max)

Declare @TamanhoSenha Int,
@QuantidadeSenha Int

Set @QuantidadeSenha = 10 --Quantidade de senhas para serem geradas
Set @GrupoCharMaiuscula = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
Set @GrupoCharMinuscula = 'abcdefghijklmnopqrstuvwxyz'
Set @GrupoCharNumericos = '1234567890'
Set @GrupoCharEspeciais = '!#%&()*+,-./:;<=>@[]^_{}~'
Set @GrupoCharDiferenci = ''

Set @GrupoBitMaiuscula = 1
Set @GrupoBitMinuscula = 1
Set @GrupoBitNumericos = 1
Set @GrupoBitEspeciais = 1
Set @GrupoBitDiferenci = 0

Set @GrupoCharTotal = Case when @GrupoBitMaiuscula = 1 Then @GrupoCharMaiuscula Else '' End +
Case when @GrupoBitMinuscula = 1 Then @GrupoCharMinuscula Else '' End +
Case when @GrupoBitNumericos = 1 Then @GrupoCharNumericos Else '' End +
Case when @GrupoBitEspeciais = 1 Then @GrupoCharEspeciais Else '' End +
Case when @GrupoBitDiferenci = 1 Then @GrupoCharDiferenci Else '' End

While @QuantidadeSenha > 0
Begin
set @tmpTextoSenha = ''

Set @TamanhoSenha = 15 --Tamanho da senha em Caracteres

While @TamanhoSenha > 0
Begin
Select @tmpTextoSenha = @tmpTextoSenha + SUBSTRING(@GrupoCharTotal , Cast((Len(@GrupoCharTotal) - 2) * RAND(CAST(NEWID() AS varbinary )) + 1 as int) , 1)
Set @TamanhoSenha = @TamanhoSenha - 1
End
Insert Into @TextoSenha Values (@tmpTextoSenha)
Set @QuantidadeSenha = @QuantidadeSenha - 1
End

Select * from @TextoSenha

Random no SQLServer

A função Rand no SQLServer, assim como descriton o BOL, "Retorna um valor float pseudo-aleatório de 0 a 1, exclusivo" e "Chamadas repetitivas de RAND() com o mesmo valor de semente retornam os mesmos resultados. ". Ou seja, de Randomico não tem praticamente nada.

Mas dá para contornar isso usando alguns artificios.

Exemplo, execute algumas vezes repetidas esta linha:
SELECT RAND(), RAND(3);

Os resultados não mudam.

Idem Aqui:
SELECT RAND(159784);
SELECT RAND(159784);
SELECT RAND(159784);

Aqui vou apresentar 2 meios (Existem vários) de fazer com que o Rand realmente seja um Rand, utilizando seu parametro de seed.

1) Usando uma Data:

SELECT RAND( (DATEPART(mm, GETDATE()) * 100000 )
+ (DATEPART(ss, GETDATE()) * 1000 )
+ DATEPART(ms, GETDATE()) );

2) Usando o o NewID:


SELECT RAND(CAST( NEWID() AS varbinary ));


Com isso em mãos, é possivel então estabelecer limites Mínimos e Máximos para o Random:


DECLARE @Upper INT;
DECLARE @Lower INT


SET @Lower = 1 ---- The lowest random number
SET @Upper = 999 ---- The highest random number

--Para gerar Randomicamente os números em float:
SELECT Cast((@Upper - @Lower -1) * RAND(CAST(NEWID() AS varbinary)) + @Lower as Float)

--Para gerar Randomicamente os números em Int:
SELECT Cast((@Upper - @Lower -1) * RAND(CAST( NEWID() AS varbinary )) + @Lower as Int)

Gerar Senhas Randomicas parte 1.

Este código gera senhas randomicas usando como suporte a spt_values.


declare @password varchar(15) -- Aqui o 15 é a quantidade de caracteres

set @password=''

select @password=@password+char(n) from
(
Select Top 15 Number as N -- Aqui o 15 é a quantidade de caracteres
From master..spt_values
Where type='p'
And number between 48 and 122
Order by newid()
) as t

select @password

quarta-feira, 26 de agosto de 2009

Retornar os valores afetados nos Inserts, Updates ou Deletes.

Outra forma mais profissional de se fazer o que foi descrito no post ("Update em Tabela e em variavel ao mesmo tempo.") é utilizando a cláusula OUTPUT do Insert, Update ou Delete.

Ela contém os valores Deleted e Inserted de todas as colunas do update, independente da quantidade de linhas que o Insert, Update ou Delete afetaram:


create table #permanent(id int, d float, comment varchar(15))
go
insert into #permanent values(1, 10., 'Original Row')
insert into #permanent values(2, 10., 'Original Row')
insert into #permanent values(3, 10., 'Original Row')
go


create table #staging(id int, d float)
go
insert into #staging values(2, 15.)
insert into #staging values(3, 15.)
insert into #staging values(4, 15.)
go


declare @updated_ids table(id int, d float, commentAnt varchar(15), commentNew varChar(15))

update #permanent
set d = s.d, comment = 'Modified Row'
output inserted.id, inserted.d, deleted.comment, inserted.comment into @updated_ids
from #permanent p, #staging s
where p.id=s.id


Select *
From @updated_ids


Drop Table #staging
Drop Table #permanent

Split parte 3

Assim como o post Split parte 2, este exemplo quebra uma linha separada por ";" em linhas usando recursos do XQuery, porém a partir de uma tabela e não de uma variavel com valores fixos.




Create Table #tmpLista (idLinha Int, xCol VarChar(max))
Insert Into #tmpLista Values (1, 'Linha A1;Linha B1;Linha C1;Linha D1;Linha E1;Linha F1')
Insert Into #tmpLista Values (2, 'Linha A2;Linha B2;Linha C2;Linha D2;Linha E2;Linha F2')
Insert Into #tmpLista Values (3, 'Linha A3;Linha B3;Linha C3;Linha D3;Linha E3;Linha F3')
Insert Into #tmpLista Values (4, 'Linha A4;Linha B4;Linha C4;Linha D4;Linha E4;Linha F4')
Insert Into #tmpLista Values (5, 'Linha A5;Linha B5;Linha C5;Linha D5;Linha E5;Linha F5')
Insert Into #tmpLista Values (6, 'Linha A6;Linha B6;Linha C6;Linha D6;Linha E6;Linha F6')
Insert Into #tmpLista Values (7, 'Linha A7;Linha B7;Linha C7;Linha D7;Linha E7;Linha F7')
Insert Into #tmpLista Values (8, 'Linha A8;Linha B8;Linha C8;Linha D8;Linha E8;Linha F8')
Insert Into #tmpLista Values (9, 'Linha A9;Linha B9;Linha C9;Linha D9;Linha E9;Linha F9')
Insert Into #tmpLista Values (10, 'Linha A10;Linha B10;Linha C10;Linha D10;Linha E10;Linha F10')
DECLARE @delimiter NVARCHAR(5)
Set @delimiter = ';'
;with tbLista as (
SELECT CAST ( '' + REPLACE ( xCol , @delimiter , '' ) + '' AS xml ) AS xCol, idLinha
From #tmpLista
Where idLinha Between 1 and 5
)
SELECT T.split.value('.', 'nvarchar(max)') AS data, idLinha
FROM tbLista CROSS APPLY xCol.nodes('/d') T (split)

Drop Table #tmpLista

Update em Tabela e em variavel ao mesmo tempo.

Me perguntaram como poderiam fazer um update de uma tabela e ao mesmo tempo um "set" de uma variavel com o valor antigo do update.

Achei meio sem sentido, talvez só para economizar um select, mas de qualquer jeito posto aqui:


Create Table #Tabela1 (id Int, Nome Varchar(50))

Insert Into #Tabela1 Values (1, 'AAA')
Insert Into #Tabela1 Values (2, 'BBB')
Insert Into #Tabela1 Values (3, 'CCC')
Insert Into #Tabela1 Values (4, 'DDD')
Insert Into #Tabela1 Values (5, 'EEE')

Declare @Nome VarChar(50)

Update #Tabela1
Set @Nome = Nome,
Nome = 'AAA1'
Where id = 1

Select @Nome as 'Antigo'

Select * from #Tabela1 Where id = 1

Drop Table #Tabela1

terça-feira, 25 de agosto de 2009

Join Array - Concatenar registros parte 2

Este exemplo, utilizando recursos simples do SQLServer, faz um "join" dos registros em uma string separados por ";" em uma variavel.


DECLARE @Procedures varchar(MAX)

Select @Procedures = COALESCE(@Procedures + ', ', '') + name
From sysobjects
Where name like 'sp_%'

Select @Procedures

Join Array - Concatenar registros parte 1

Este é outro exemplo utilizando os recursos de XML do SQLServer. Ele faz um "join" dos registros em uma string separados por ";"

A rigor só a query abaixo já faz o "join", mas ela retorna como XML:

Select cast(name as varchar) + '; ' AS [text()]
from dbo.sysobjects
where name like 'sp_%'
For XML PATH ('')


Então para retornar em String:


Select (Select cast(name as varchar) + '; ' AS [text()]
from dbo.sysobjects
where name like 'sp_%'
For XML PATH ('')) as Array



Split parte 2

Esse exemplo quebra uma linha separada por ";" em linhas, também usando recursos do XQuery.


DECLARE @data NVARCHAR(MAX), @delimiter NVARCHAR(5)

DECLARE @textXML XML;

Set @data = 'Linha A1;Linha B1;Linha C1;Linha D1;Linha E1;Linha F1'

SELECT @textXML = CAST('' + REPLACE(@data, ';', '') + '' AS XML);

SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM @textXML.nodes('/d') T (split)

Split parte 1

Esse exemplo quebra uma linha separada por ";" em colunas, usando recursos do XQuery.


Create Table #tmpLista (xCol VarChar(max))
Insert Into #tmpLista Values ('Linha A1;Linha B1;Linha C1;Linha D1;Linha E1;Linha F1')
Insert Into #tmpLista Values ('Linha A2;Linha B2;Linha C2;Linha D2;Linha E2;Linha F2')
Insert Into #tmpLista Values ('Linha A3;Linha B3;Linha C3;Linha D3;Linha E3;Linha F3')
Insert Into #tmpLista Values ('Linha A4;Linha B4;Linha C4;Linha D4;Linha E4;Linha F4')
Insert Into #tmpLista Values ('Linha A5;Linha B5;Linha C5;Linha D5;Linha E5;Linha F5')
Insert Into #tmpLista Values ('Linha A6;Linha B6;Linha C6;Linha D6;Linha E6;Linha F6')
Insert Into #tmpLista Values ('Linha A7;Linha B7;Linha C7;Linha D7;Linha E7;Linha F7')
Insert Into #tmpLista Values ('Linha A8;Linha B8;Linha C8;Linha D8;Linha E8;Linha F8')
Insert Into #tmpLista Values ('Linha A9;Linha B9;Linha C9;Linha D9;Linha E9;Linha F9')
Insert Into #tmpLista Values ('Linha A10;Linha B10;Linha C10;Linha D10;Linha E10;Linha F10')

;WITH tmpListaDados (DadosXML) AS (
SELECT CAST('' + REPLACE(xCol,';','') + '' AS XML)
FROM #tmpLista)
SELECT CAST(DadosXML.query('for $coluna in /N return $coluna/n[1]/text()') AS VARCHAR(max)) As Coluna1,
CAST(DadosXML.query('for $coluna in /N return $coluna/n[2]/text()') AS VARCHAR(max)) As Coluna2,
CAST(DadosXML.query('for $coluna in /N return $coluna/n[3]/text()') AS VARCHAR(max)) As Coluna3,
CAST(DadosXML.query('for $coluna in /N return $coluna/n[4]/text()') AS VARCHAR(max)) As Coluna4,
CAST(DadosXML.query('for $coluna in /N return $coluna/n[5]/text()') AS VARCHAR(max)) As Coluna5,
CAST(DadosXML.query('for $coluna in /N return $coluna/n[6]/text()') AS VARCHAR(max)) As Coluna6
FROM tmpListaDados

Drop Table #tmpLista

O que o seu SQLServer está executando agora?

Estas duas querys mostram exatamente o que está sendo executado nas sessões ativas do banco de dados.

É bastante útil quando você está com problemas de execução no banco e precisa saber o que está causando os problemas.


/********************************************************/
/* Mosta o SQL que cada sessão está executando */
/********************************************************/

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
go
SELECT [Spid] = session_Id
, ecid
, [Database] = DB_NAME(sp.dbid)
, [User] = nt_username
, [Status] = er.status
, [Wait] = wait_type
, [Individual Query] = SUBSTRING (qt.text,
er.statement_start_offset/2,
(CASE WHEN er.statement_end_offset = -1
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
ELSE er.statement_end_offset END -
er.statement_start_offset)/2)
,[Parent Query] = qt.text
, Program = program_name
, Hostname
, nt_domain
, start_time
FROM sys.dm_exec_requests er
INNER JOIN sys.sysprocesses sp ON er.session_id = sp.spid
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle)as qt
WHERE session_Id > 50 -- Ignore system spids.
AND session_Id NOT IN (@@SPID) -- Ignore this current statement.
ORDER BY 1, 2

SELECT SDER.[statement_start_offset],
SDER.[statement_end_offset],
CASE
WHEN SDER.[statement_start_offset] > 0 THEN
--The start of the active command is not at the beginning of the full command text
CASE SDER.[statement_end_offset]
WHEN -1 THEN
--The end of the full command is also the end of the active statement
SUBSTRING(DEST.TEXT, (SDER.[statement_start_offset]/2) + 1, 2147483647)
ELSE
--The end of the active statement is not at the end of the full command
SUBSTRING(DEST.TEXT, (SDER.[statement_start_offset]/2) + 1, (SDER.[statement_end_offset] - SDER.[statement_start_offset])/2)
END
ELSE
--1st part of full command is running
CASE SDER.[statement_end_offset]
WHEN -1 THEN
--The end of the full command is also the end of the active statement
RTRIM(LTRIM(DEST.[text]))
ELSE
--The end of the active statement is not at the end of the full command
LEFT(DEST.TEXT, (SDER.[statement_end_offset]/2) +1)
END
END AS [executing statement],
DEST.[text] AS [full statement code]
FROM sys.[dm_exec_requests] SDER CROSS APPLY sys.[dm_exec_sql_text](SDER.[sql_handle]) DEST
WHERE SDER.session_id > 50
ORDER BY SDER.[session_id], SDER.[request_id]

Estatísticas e ShowPlan de uma Query.

Para exibir as estatísticas e o show plan de uma query:

Os exemplos abaixo mostram os tipos de estatísticas.
/**************************************************************************************************************//* *//* Statistics */ /* */ /**************************************************************************************************************/

SET STATISTICS TIME ON
go
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
go
SET STATISTICS TIME Off

SET STATISTICS IO ON;

GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
GO
SET STATISTICS IO OFF;


SET STATISTICS PROFILE On
GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
GO
SET STATISTICS PROFILE Off

SET STATISTICS XML ON

GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
GO
SET STATISTICS XML Off

/**************************************************************************************************************//* *//* ShowPlan */ /* */ /**************************************************************************************************************/
SET SHOWPLAN_ALL ON;

GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Hash Join sys.objects B On B.object_id = A.id

GO
SET SHOWPLAN_ALL OFF;
GO

SET SHOWPLAN_TEXT ON;

GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Join sys.objects B On B.object_id = A.id
GO
Select B.object_id, B.name as Tabela, A.name from syscolumns A Inner Hash Join sys.objects B On B.object_id = A.id

GO
SET SHOWPLAN_TEXT Off;
GO


quinta-feira, 20 de agosto de 2009

Listar arquivos do diretório.

Para listar os arquivos de alguma pasta do servidor ou de outra maquina da rede, a partir do SQLServer 2000 é possivel usar uma das Stored Procedures não documentada.

Há outras opções, entre elas utilizando a xp_cmdshell e utilizando FSO. Mas de longe esta é a mais simples.

No exemplo a seguir, ele vai listar todos os arquivos e pasta do c:\

Parametros:

1) Pasta inicial: caminho da pasta inicial para pesquisa
2) Nivel da busca: 1 é somente a pasta inicial, 0 ou Null são todas as pastas a partir da pasta inicial informada, qualquer valor > 1 é a quantidade de niveis de subpastas a partir da pasta inicial informada.
3) Inclui arquivo: 1 Inclui arquivos e pastas; 0 ou Null só pastas.

EXEC master.dbo.xp_dirtree N'c:\', 1, 1

O retorno é:

1) SubDirectory - Nome da pasta ou arquivo
2) Depth - Nivel do arquivo em relação à pasta inicial da pesquisa.
3) File - 1 para arquivo e 0 para Pasta


Ela vale tambem para diretórios de maquinas da rede, desde que estejam compartilhadas (não precisam estar mapeadas)

EXEC master.dbo.xp_dirtree N'\\Servidor_de_rede\comum', 1, 1

Espaço em disco 3

Esta solução realmente é a que eu menos gosto, mas é bastante utilizada por usar a xp_cmdshell.

No exemplo abaixo, utilizando a xp_cmdshell a rotina executa a aplicação DOS fsutil retornando o espaço livre do disco C:. Este retorno é gravado em uma tabela temporária e depois separado e mostrado.


DECLARE @Drive varchar(max),
@SQL VARCHAR(max)

SET @SQL = 'EXEC XP_CMDSHELL "fsutil volume diskfree c:"'


Create Table #tmpLista (xCol VarChar(max))


Insert Into #tmpLista
Exec(@SQL)


Delete #tmpLista Where xCol is null


;WITH tmpDrivers (DadosXML) AS (
SELECT CAST('' + REPLACE(xCol,':','') + '' AS XML)
FROM #tmpLista)
SELECT CAST(DadosXML.query('for $coluna in /N return $coluna/n[1]/text()') AS VARCHAR(80)) As Descricao,
--CAST(DadosXML.query('for $coluna in /N return $coluna/n[last()]/text()') AS VARCHAR(80)) As Tamanho
CAST(DadosXML.query('for $coluna in /N return $coluna/n[2]/text()') AS VARCHAR(80)) As Tamanho
FROM tmpDrivers

Drop Table #tmpLista

Espaço em disco 2

Esta é sem dúvida a mais complicada, visto que, utiliza objetos do servidor (Scripting.FileSystemObjects) com a SP sp_OACreate. Porém é a que mais traz dados completos.



IF EXISTS (SELECT name FROM tempdb..sysobjects WHERE name = '##_DriveSpace')
DROP TABLE ##_DriveSpace
IF EXISTS (SELECT name FROM tempdb..sysobjects WHERE name = '##_DriveInfo')
DROP TABLE ##_DriveInfo

CREATE TABLE ##_DriveSpace
(
DriveLetter CHAR(1) not null
, FreeSpace VARCHAR(10) not null
)
CREATE TABLE ##_DriveInfo
(
DriveLetter CHAR(1)
, TotalSpace bigint
, FreeSpace bigint
, Label varchar(10)
)

DECLARE @Result INT
, @objFSO INT
, @Drv INT
, @cDrive VARCHAR(13)
, @Size VARCHAR(50)
, @Free VARCHAR(50)
, @Label varchar(10)


INSERT INTO ##_DriveSpace
EXEC master.dbo.xp_fixeddrives

-- Iterate through drive letters.
DECLARE curDriveLetters CURSOR
FOR SELECT driveletter FROM ##_DriveSpace

DECLARE @DriveLetter char(1)

OPEN curDriveLetters

FETCH NEXT FROM curDriveLetters INTO @DriveLetter

WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN

SET @cDrive = 'GetDrive("' + @DriveLetter + '")'

EXEC @Result = sp_OACreate 'Scripting.FileSystemObject', @objFSO OUTPUT

IF @Result = 0
EXEC @Result = sp_OAMethod @objFSO, @cDrive, @Drv OUTPUT

IF @Result = 0
EXEC @Result = sp_OAGetProperty @Drv,'TotalSize', @Size OUTPUT

IF @Result = 0
EXEC @Result = sp_OAGetProperty @Drv,'FreeSpace', @Free OUTPUT

IF @Result = 0
EXEC @Result = sp_OAGetProperty @Drv,'VolumeName', @Label OUTPUT

IF @Result <> 0
EXEC sp_OADestroy @Drv
EXEC sp_OADestroy @objFSO
SET @Size = (CONVERT(BIGINT,@Size) / 1048576 )
SET @Free = (CONVERT(BIGINT,@Free) / 1048576 )
INSERT INTO ##_DriveInfo
VALUES (@DriveLetter, @Size, @Free, @Label)
END

FETCH NEXT FROM curDriveLetters INTO @DriveLetter
END

CLOSE curDriveLetters
DEALLOCATE curDriveLetters

PRINT 'Drive information for server ' + @@SERVERNAME + '.'
PRINT ''

-- Produce report.
SELECT DriveLetter
, Label
, FreeSpace AS [FreeSpace MB]
, (TotalSpace - FreeSpace) AS [UsedSpace MB]
, TotalSpace AS [TotalSpace MB]
, ((CONVERT(NUMERIC(9,0),FreeSpace) / CONVERT(NUMERIC(9,0),TotalSpace)) * 100) AS [Percentage Free]
FROM ##_DriveInfo
ORDER BY [DriveLetter] ASC
GO

DROP TABLE ##_DriveSpace
DROP TABLE ##_DriveInfo

Espaço em disco 1

Existem algumas formas de se saber o espaço em disco total ou livre do servidor. Esta é uma delas:

Ela executa a Stored Procedure xp_fixeddrives que retorna a lista de drivers do servidor e o espaço livre de cada um, insere estes dados em uma tabela temporaria, para depois cruzar com a informação da function fn_servershareddrives, que mostra se é um drive Shared.


CREATE TABLE #DriveSpace ( Drive char(1), MBFree int )

-- get availabe drives and free space on each of them
INSERT INTO #DriveSpace EXEC xp_fixeddrives

SELECT * INTO #DriveUnion FROM (
SELECT A.DriveName COLLATE Latin1_General_CI_AS [Drives Available],
#DriveSpace.MBFree, 'X'[Shared]
FROM ::fn_servershareddrives() A
LEFT JOIN #DriveSpace on A.DriveName COLLATE Latin1_General_CI_AS = #DriveSpace.drive
UNION
SELECT Drive COLLATE Latin1_General_CI_AS [Drives Available],
MBFree, '-'[Shared]
FROM #DriveSpace
WHERE Drive NOT IN (SELECT DriveName COLLATE Latin1_General_CI_AS
FROM ::fn_servershareddrives()) ) A

Select * from #DriveUnion


Drop Table #DriveUnion
Drop Table #DriveSpace

Executar comandos DOS no SQLServer

A stored procedure xp_cmdshell é utilizada para executar comandos no DOS pelo SQLServer
Por default o SQLServer vem com opção de uso da xp_cmdshell desabilitado, sendo necessário habilitar manualmente pelo administrador do banco.
A liberação da xp_cmdshell implica em alguns riscos de segurança, pesquise sobre os riscos e avalie sua necessidade.
Quando é chamado por um usuário que não é um membro da função de servidor fixa sysadmin, xp_cmdshell se conecta ao Windows usando o nome da conta e a senha armazenadas na credencial chamada ##xp_cmdshell_proxy_account##. Se essa credencial de proxy não existir, xp_cmdshell falhará.
Para habilitar a opção de uso da xp_cmdshell:
sp_configure 'xp_cmdshell', '1'
GO
Reconfigure
Go

Aqui vai um exemplo usando o comando Dir C:\*.*:

Declare @DirCmd as Varchar(100)
Declare @Return as Varchar(Max)
Drop Table #dirlist
CREATE TABLE #dirlist ([FName] NVARCHAR(1000), [DS] VARCHAR(14), [CreateDate] DATETIME)
SET @DirCmd = 'dir C:\*.*'
EXEC @Return = master..xp_cmdshell @DirCmd

Várias informações do seu servidor

Mais informações detalhadas sobre seu servidor:

Select
SERVERPROPERTY('BuildClrVersion') BuildClrVersion,
SERVERPROPERTY('Collation') Collation,
SERVERPROPERTY('CollationId') CollationId,
SERVERPROPERTY('ComparisonStyle') ComparisonStyle,
SERVERPROPERTY('ComputerNamePhysicalNetBIOS') ComputerNamePhysicalNetBIOS,
SERVERPROPERTY('Edition') Edition,
SERVERPROPERTY('EditionID') EditionID,
SERVERPROPERTY('EngineEdition') EngineEdition,
SERVERPROPERTY('InstanceName') InstanceName,
SERVERPROPERTY('IsClustered') IsClustered,
SERVERPROPERTY('IsFullTextInstalled') IsFullTextInstalled,
SERVERPROPERTY('IsIntegratedSecurityOnly') IsIntegratedSecurityOnly, SERVERPROPERTY('IsSingleUser') IsSingleUser,
SERVERPROPERTY('LCID') LCID,
SERVERPROPERTY('LicenseType') LicenseType,
SERVERPROPERTY('MachineName') MachineName,
SERVERPROPERTY('NumLicenses') NumLicenses,
SERVERPROPERTY('ProcessID') ProcessID,
SERVERPROPERTY('ProductVersion') ProductVersion,
SERVERPROPERTY('ProductLevel') ProductLevel,
SERVERPROPERTY('ResourceLastUpdateDateTime') ResourceLastUpdateDateTime,
SERVERPROPERTY('ResourceVersion') ResourceVersion,
SERVERPROPERTY('ServerName') ServerName,
SERVERPROPERTY('SqlCharSet') SqlCharSet,
SERVERPROPERTY('SqlCharSetName') SqlCharSetName,
SERVERPROPERTY('SqlSortOrder') SqlSortOrder,
SERVERPROPERTY('SqlSortOrderName') SqlSortOrderName,
SERVERPROPERTY('FilestreamShareName') FilestreamShareName,
SERVERPROPERTY('FilestreamConfiguredLevel') FilestreamConfiguredLevel,
SERVERPROPERTY('FilestreamEffectiveLevel') FilestreamEffectiveLevel
GO


A xp_msver retorna:

ProductName
ProductVersion
Language
Platform
Comments
CompanyName
FileDescription
FileVersion
InternalName
LegalCopyright
LegalTrademarks
OriginalFilename
PrivateBuild
SpecialBuild
WindowsVersion
ProcessorCount
ProcessorActiveMask
ProcessorType
PhysicalMemory
Product ID

exec master..xp_msver
GO



A sp_helpserver retorna entre outras informações o collation do banco, sempre o id=0 é seu banco atual

exec master..sp_helpserver


A sp_server_info retorna:

DBMS_NAME
DBMS_VER
OWNER_TERM
TABLE_TERM
MAX_OWNER_NAME_LENGTH
TABLE_LENGTH
MAX_QUAL_LENGTH
COLUMN_LENGTH
IDENTIFIER_CASE
TX_ISOLATION
COLLATION_SEQ
SAVEPOINT_SUPPORT
MULTI_RESULT_SETS
ACCESSIBLE_TABLES
USERID_LENGTH
QUALIFIER_TERM
NAMED_TRANSACTIONS
SPROC_AS_LANGUAGE
ACCESSIBLE_SPROC
MAX_INDEX_COLS
RENAME_TABLE
RENAME_COLUMN
DROP_COLUMN
INCREASE_COLUMN_LENGTH
DDL_IN_TRANSACTION
DESCENDING_INDEXES
SP_RENAME
REMOTE_SPROC
SYS_SPROC_VERSION


exec master..sp_server_info


Log no Trace do Banco de Dados


Assim como o Event Viewer, em alguns processo é bom gravar algo no Trace do do servidor.

E esse input de evento no Trace é feita com a sp_trace_generateevent

1) event_id - Numero do evento, ele deve ser entre 82 e 91 e deve ser previamente criado pela sp_trace_setevent

2) userinfo - Opcional, uma descrição de até 128 posições sobre o evento.

3) userdata - Opcional, uma descrição de até 8000 posições VarBinary do evento.


EXEC master..sp_trace_generateevent 82, 'Algum evento que mereça uma entrada manual no Trace'

Log no Event Viewer pelo SQLServer.


Em alguns processo é bom gravar algo no Event Viewer do servidor, isso é uma prática muito utilizada por programadores de sistemas em suas aplicações.


No SQLServer tambem dá para gravar Logs no Event Viewer.


1) error_number - É um número de erro definido pelo usuário maior que 50.000. O valor máximo é 2147483647 (2^31 - 1).

2) message - É uma cadeia de caracteres com no máximo 2048 caracteres.

3) severity - É uma destas três cadeias de caracteres: INFORMATIONAL, WARNING ou ERROR. severity é opcional, com um padrão INFORMATIONAL.

EXEC xp_logevent 60000, 'Este é um teste de de Log Event pelo SQLServer', INFORMATIONAL

Lá no servidor o resultado no Event Viewer foi:

Event Type: Information
Event Source: MSSQLSERVER
Event Category: (2)
Event ID: 17061
Date: 20/8/2009
Time: 18:34:06
User: **********
Computer: **********
Description:
Error: 60000 Severity: 10 State: 1 Este é um teste de de Log Event pelo SQLServer
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

Ler o Log do banco de dados via query

Quer ler o Log do seu banco de dados via query sem precisar abrir o arquivo?
Existe uma Stored Procedure não documentada no SQLServer que faz isso.
Lembramos que as Stored Procedure não documentadas a MS não garante sua continuidade em próximas versões e nem seu funcionamento perfeito.


Os parâmetros de entrada dela são:
1) Número do arquivop de Log: 0 = Atual, 1 = Arquivo #1, 2 = Arquivo #2, etc...
2) Tipo do arquivo de Log: 1 or NULL = Log de Erro, 2 = Log do SQL Agent
3) Search string 1: String para busca nos resultados
4) Search string 2: String para refinamento da busca
5) Data inicial para busca
6) Data final para busca
7) Ordenação dos resultados: N'asc' = ascending, N'desc' = descending

EXEC master.dbo.xp_readerrorlog 0, 1, Null, Null, Null, Null, N'desc'

Msg 0, Level 11, State 0, Line 0 / Msg 0, Level 20, State 0, Line 0

Já peguei esses erros no SQLServer 2005 com SP2 ou menor. Ele simplesmente mata sua conexão e acontece normalmente quando são utilizadas as functions
sys.dm_exec_sql_text e sys.dm_exec_query_plan com qualquer Cross Join.


O remédio é instalar o Service Pack 3 ou posterior quando houver para o SQLServer 2005.

Msg 0, Level 11, State 0, Line 0
Erro grave no comando atual. Os resultados, se houver algum, deverão ser descartados.
Msg 0, Level 20, State 0, Line 0
Erro grave no comando atual. Os resultados, se houver algum, deverão ser descartados.

O erro é falta do Service Pack:
http://support.microsoft.com/kb/910416/en-us#top

você sabe quais são as Querys mais custosas do seu Banco?

Pois é..... aqui estão elas, ordenadas por total_logical_reads e com o query plan de cada uma (em xml)

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


View de Gerenciamento Dinâmico sys.dm_os_ring_buffers

A View sys.dm_os_ring_buffers é uma DMV (Dynamic Management Views) muito boa para colher informações sobre a "saúde" do seu servidor.

Apesar de ser um pouco chato a extração de informações do campo Record (xml), vale a pena estar sempre de olho nas informações que esta view traz.

As informações são divididas por tipos (ring_buffer_type):


RING_BUFFER_RESOURCE_MONITOR – memory state changes due to various types of memory pressure

RING_BUFFER_MEMORY_BROKER – notification to components by memory broker advising them to grow, shrink or stay stable

RING_BUFFER_SINGLE_PAGE_ALLOCATOR – when the Buffer Pool single page allocator turns on/off internal memory pressure

RING_BUFFER_OOM – out-of-memory conditions

RING_BUFFER_BUFFER_POOL – severe buffer pool failures, including
buffer pool out-of-memory conditions

RING_BUFFER_SCHEDULER – scheduler operations

RING_BUFFER_SCHEDULER_MONITOR – scheduler health

RING_BUFFER_EXCEPTION – list of exceptions

RING_BUFFER_CLRAPPDOMAIN – state of AppDomains loaded in the system

RING_BUFFER_SECURITY_ERROR – (new in SQL Server 2005 SP2) Windows
API failure information


No post anterior tem um exemplo de como utilizar esta View para verificar o processamento do servidor.

Como está o processamento do servidor.

Usando a tabela de sistema sys.dm_os_ring_buffers, você consegue saber como está o processador do servidor nos últimos 256 minutos.

Estes dados são percentuais entre Idle (Livre), Utilizados pelo SQL e Utilizados por outros processos do sistema (Não SQLServer).

Esta informação combinadas com algumas estatisticas e traces podem te dar uma bela ideia de como está se comportando sua base, por horarios, querys, atividades, etc... e até se ha necessidade de intervensão física no servidor.

declare @ts_now bigint

select @ts_now = cpu_ticks / convert(float, cpu_ticks_in_ms) from sys.dm_os_sys_info

select record_id, dateadd(ms, -1 * (@ts_now - [timestamp]), GetDate()) as EventTime, SQLProcessUtilization, SystemIdle, 100 - SystemIdle - SQLProcessUtilization as OtherProcessUtilization from (select record.value('(
./Record/@id)[1]', 'int') as record_id, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') as SystemIdle, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') as SQLProcessUtilization, timestamp from (select timestamp, convert(xml, record) as record from sys.dm_os_ring_buffers where ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' and record like '%%') as x ) as y order by record_id desc

Miudezas Parte 3: Transformar TimeStamp em DateTime

As vezes é necessário transformar um TimeStamp que é um varbinary em DateTime (como no post seguinte), isso é meio chato de fazer, mas segue um dos modos:


Select dateadd(ms, -1 * @@DBTS, GetDate()) as EventTime

Separar Nome e Sobrenome

Este código eu peguei do blog do MVP Gustavo Maia Aguiar, que você deve visitar obrigatoriamente!!!


Ele transfoma a string em XML para poder utilizar os comandos XQuery.

No NomeXML.query você nota a $nome/n[1]/text() que pega a primeira parte de nome e o $nome/n[last()]/text() que pega somente a última. Essa solução eu vou usar mais para frente para fazer um "Split" de uma string no SQLServer.


CREATE TABLE #Nomes (Nome VARCHAR(80))
-- Inserção de registros
INSERT INTO #Nomes VALUES ('Joana Maria')

INSERT INTO #Nomes VALUES ('Ana Paula Silveira')
INSERT INTO #Nomes VALUES ('Pedro Paulo Almeida')
INSERT INTO #Nomes VALUES ('Carlos Eduardo da Silva')
INSERT INTO #Nomes VALUES ('Rodrigo Diógenes Cunha Meira')
INSERT INTO #Nomes VALUES ('Maria')


;WITH NomesF (Nome, NomeXML) AS
( SELECT Nome, CAST('' + REPLACE(Nome,' ','') + '' AS XML) FROM #Nomes)
SELECT Nome,
CAST(NomeXML.query('for $nome in /N return $nome/n[1]/text()') AS VARCHAR(80)) As PrimeiroNome,
CAST(NomeXML.query('for $nome in /N return $nome/n[last()]/text()') AS VARCHAR(80)) As SobreNome,
CAST(NomeXML.query('for $nome in /N return concat($nome/n[1],'' '',$nome/n[last()])') AS VARCHAR(80)) As NomeAbreviado,
LEFT(Nome, Case When CHARINDEX(' ',Nome) = 0 Then Len(Nome) Else CHARINDEX(' ',Nome) -1 End) as UsingReverse1,
RIGHT(Nome, Case When CHARINDEX(' ',REVERSE(Nome)) = 0 Then 0 Else CHARINDEX(' ',REVERSE(Nome))-1 end) as UsingReverse2
FROM NomesF


Como deixar uma Stored Procedure como system sp

Um das vantagens de ter uma Stored Procedure como system sp é poder chama-la de qualquer banco de dados do servidor. Isso é realmente muito útil para stored procedures com códigos mais genéricos e com relativa importancia.

Para isso sua Stored Procedure deve ser criada no banco Master.

EXEC sp_ms_marksystemobject 'nome_procedure'
GO


Para verificar se a Stored procedure está como system:


SELECT NAME, IS_MS_SHIPPED
FROM SYS.OBJECTS
WHERE NAME = 'nome_procedure'
GO

Calendário em SQLServer

Achei na Net esse código, achei bem interessante.
Alem de ser um excelente exemplo de CTE com recursividade.


declare @start datetime,
@end datetime
set @start = '2009-01-01'
set @end = '2010-01-01'
;with calendar(date,isweekday, y, q,m,d,dw,monthname,dayname,w) as
(
select @start ,
case when datepart(dw,@start) in (1,7) then 0 else 1 end,
year(@start),
datepart(qq,@start),
datepart(mm,@start),
datepart(dd,@start),
datepart(dw,@start),
datename(month, @start),
datename(dw, @start),
datepart(wk, @start)
union all
select date + 1,
case when datepart(dw,date + 1) in (1,7) then 0 else 1 end,
year(date + 1),
datepart(qq,date + 1),
datepart(mm,date + 1),
datepart(dd,date + 1),
datepart(dw,date + 1),
datename(month, date + 1),
datename(dw, date + 1),
datepart(wk, date + 1) from calendar where date + 1< @end
)
select * from calendar option(maxrecursion 10000)

Campos Identity

Este post mostra alguns métodos para trabalhar com campos Identitys.

Primeiro, estra query mostra como estão os Identitys de todas as tabelas do seu banco de dados, a tabela, coluna, o tipo, o valor atual e o % de uso:


SELECT QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) AS TableName,
c.name AS ColumnName,
CASE c.system_type_id
WHEN 127 THEN 'bigint'
WHEN 56 THEN 'int'
WHEN 52 THEN 'smallint'
WHEN 48 THEN 'tinyint'
END AS 'DataType',
IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) AS CurrentIdentityValue,
CASE c.system_type_id
WHEN 127 THEN (IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) * 100.) / 9223372036854775807
WHEN 56 THEN (IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) * 100.) / 2147483647
WHEN 52 THEN (IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) * 100.) / 32767
WHEN 48 THEN (IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) * 100.) / 255
END AS 'PercentageUsed'
FROM sys.columns AS c
INNER JOIN
sys.tables AS t
ON t.[object_id] = c.[object_id]
WHERE c.is_identity = 1
ORDER BY PercentageUsed DESC



Para incrementar passo (seed) a um identity e retornar o valor:
SELECT IDENT_INCR('sua_tabela') AS 'IDENT_INCR';

Para mostra o valor atual do identity:
SELECT IDENT_CURRENT('sua_tabela') AS Current_Identity;

Mostra o "valor de passo" (seed) da identity
SELECT IDENT_SEED('sua_tabela') AS 'IDENT_SEED';




Reset na Identity para o próximo valor valido na tabela.
DBCC CHECKIDENT('sua_tabela')

Reset na Identity para o valor informado no último parâmetro.
DBCC CHECKIDENT('sua_tabela', RESEED, 0)



Para desligar o Identity de uma tabela MOMENTANEAMENTE, para um insert:

SET IDENTITY_INSERT sua_tabela On --Desliga o Identity

Faça os Inserts necessários para sua manutenção/carga.

SET IDENTITY_INSERT sua_tabela Off --Religa o Identity

Você sabe a diferença entre Some, Any e All??

Com este exemplo bem simples, fica facil de entender.


CREATE TABLE #T1 (ID int)
GO
INSERT Into #T1 VALUES (1)
INSERT Into #T1 VALUES (2)
INSERT Into #T1 VALUES (3)
INSERT Into #T1 VALUES (4)



Print 'The following query returns TRUE because 3 is less than some of the values in the table.'
IF 3 < SOME (SELECT ID FROM #T1)
PRINT 'TRUE'
ELSE
PRINT 'FALSE'


Print 'The following query returns FALSE because 3 is not less than all of the values in the table.'
IF 3 < ALL (SELECT ID FROM #T1)
PRINT 'TRUE'
ELSE
PRINT 'FALSE'


Print 'The following query returns TRUE because 3 is less than anyone of the values in the table.'
Print 'SOME is an ISO standard equivalent for ANY.'
IF 3 < Any (SELECT ID FROM #T1)
PRINT 'TRUE'
ELSE
PRINT 'FALSE'


Verificar e Trocar o owner do Banco


Para listar os Bancos e seus owners:


SELECT databases.NAME as Banco,
server_Principals.NAME as Owner
FROM sys.[databases]
INNER JOIN sys.[server_principals] ON [databases].owner_sid = [server_principals].sid




Esta opção deve ser utilizada somente se o banco perdeu o owner, ou é claro, se você tem certeza do que está fazendo.

Para fazer isso você precisa ser SysAdmin ou estar no grupo DB_Owner.

Este exemplo irá trocar o owner do banco de dados atual para SA.


USE Seu_Banco_De_Dados
GO
EXEC sp_changedbowner 'sa'
GO



Detalhes da Execução de um Job.

Para saber os detalhes da execução de um Job:

Com essa Query dá para saber se o Job ainda está executando, ou se já finalizou, com sucesso ou erro, quanto tempo demorou, etc.

/***************************************/
/* Detalhes da Execução do JOB */
/***************************************/
Use msdb
GO
SELECT sjb.Job_id,
stsv.server_id,
stsv.server_name,
stsv.enlist_date,
stsv.last_poll_date,
cast(dateadd(ss, cast(substring(cast(last_run_time + 1000000 as char(7)),6,2) as int),
dateadd(mi, cast(substring(cast(last_run_time + 1000000 as char(7)),4,2) as int),
dateadd(hh, cast(substring(cast(last_run_time + 1000000 as char(7)),2,2) as int),
convert(datetime,cast (last_run_date as char(8)))))) as DateTime) as LastRunDate,
sjs.last_run_duration,
sjs.last_run_outcome,
sjs.last_outcome_message
FROM msdb.dbo.sysjobs sjb
Inner Join msdb.dbo.sysjobservers sjs On sjb.Job_Id = sjs.job_id
Left Join msdb.dbo.systargetservers_view stsv ON (sjs.server_id = stsv.server_id)
WHERE sjb.name = 'BKPIncremental-1.BKPIncremental-1'
Select Top 2 A.*
from msdb..sysjobhistory A
Inner Join msdb.dbo.sysjobs B On A.Job_Id = B.job_id
Where B.name = 'Nome do Seu Job'


Caso tenha dado erro na execução do Job, esta Query, mostra o log do erro do Job:


Select Top 2 A.*
from msdb..sysjobhistory A
Inner Join msdb.dbo.sysjobs B On A.Job_Id = B.job_id
Where B.name = 'Nome do Seu Job'


Os registros sempre vem uma linha para o Job Outcome e uma linha para cada Step do Job, como o exemplo é um Job de um único Step coloquei o Top 2.

Tamanho da tabela no Banco


Essa SP de sistema, mostra o tamanho em KB das seguintes informações da tabela:

1) Quantida de registros da tabela

2) Espaço reservado para a tabela

3) Espaço utilizado pelos dados

4) Espaço utilizado pelos Indices

5) Espaço ainda livre

sp_spaceused 'sua_tabela'

Estas informações são importantes para por exemplo rever um planejamento de capacidade do servidor.

Como está a utilização dos seus Indices?

Depois de criar os indices necessários, o ideal é verificar se realmente eles estão sendo bem aproveitados.

A grosso modo, não adianta você criar um indice em tabelas que são mais utilizadas para inserts/updates do que para consultas.

E isso "talvez" você só saiba algum tempo depois, após algumas estatisticas.

Esta primeira query mostra a quantidade de Inserts, Updates e Deletes por indices.

/*leaf_insert_count - total count of leaf level inserts leaf_delete_count - total count of leaf level inserts leaf_update_count - total count of leaf level updates
*/


SELECT OBJECT_NAME(A.[OBJECT_ID]) AS [OBJECT NAME], I.[NAME] AS [INDEX NAME], A.LEAF_INSERT_COUNT, A.LEAF_UPDATE_COUNT, A.LEAF_DELETE_COUNT FROM SYS.DM_DB_INDEX_OPERATIONAL_STATS (NULL,NULL,NULL,NULL ) A INNER JOIN SYS.INDEXES AS I ON I.[OBJECT_ID] = A.[OBJECT_ID] AND I.INDEX_ID = A.INDEX_ID WHERE OBJECTPROPERTY(A.[OBJECT_ID],'IsUserTable') = 1 And (A.LEAF_INSERT_COUNT > 0 Or A.LEAF_UPDATE_COUNT > 0 Or A.LEAF_DELETE_COUNT > 0)Order by LEAF_INSERT_COUNT DESC


Esta mostra a quantidade de Index Seeks, Index Scans, Index Lookups e operações de Insert, Update e Delete por indices.

/*user_seeks - number of index seeks
user_scans- number of index scans
user_lookups - number of index lookups
user_updates - number of insert, update or delete operations
*/


SELECT OBJECT_NAME(S.[OBJECT_ID]) AS [OBJECT NAME], I.[NAME] AS [INDEX NAME], USER_SEEKS, USER_SCANS, USER_LOOKUPS, USER_UPDATES FROM SYS.DM_DB_INDEX_USAGE_STATS AS S INNER JOIN SYS.INDEXES AS I ON I.[OBJECT_ID] = S.[OBJECT_ID] AND I.INDEX_ID = S.INDEX_ID WHERE OBJECTPROPERTY(S.[OBJECT_ID],'IsUserTable') = 1 Order by User_Updates DESC, User_Seeks ASC

Miudezas Parte 2: Saber se o SQLAgent está ativo

IF NOT EXISTS (SELECT 1 FROM master.dbo.sysprocesses WHERE program_name = N'SQLAgent - Generic Refresher')
BEGIN
PRINT 'SQL Server está ativo mas o SQL Server Agent está INATIVO'
END
ELSE
BEGIN
PRINT 'SQL Server e o SQL Server Agent estão ativos'
END

Miudezas Parte 1: Último dia do mês.

--Último dia do mês anterior
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))

--Último dia do mês atual
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))

--Último dia do próximo mês
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0))

Horas Uptime

Quer saber a quantidade de horas que seu servidor está ativo?

Os primeiros processos das tabela de sysprocesses são iniciado somente quando o banco é iniciado, e não tem como esses processos pararem ou reiniciarem sem parar o banco. Então a partir deles dá p/ saber quanto tempo seu banco está online.



SELECT 'Uptime: ' + Cast(datediff(mi, login_time, getdate()) /60 as VarChar) + ' Horas'
FROM master..sysprocesses WHERE spid = 1

Como iniciar um Job pelo DOS.

Para isso vamos utilizar o aplicativo do SQLServer para DOS OSql. Ele permite que seja executado qualquer instrução ou arquivo sql apenas pelo prompt do DOS, com funcionamento muito semelhante ao SQLCmd (porém muito mais simples) veremos o funcionamento dele mais detalhadamente em um post mais adiante.

Além do OSql, vamos utilizar o Stored Procedure de sistema "sp_start_job" do banco msdb para iniciar o Job.

Vamos supor que o Job que você quer iniciar se chama Job1. Então, no prompt do DOS:

osql -S "NOME_DO_SERVIDOR" -E -Q"exec msdb.dbo.sp_start_job 'Job1'"

Como executar uma Procedure automaticamente no Start do banco.


Algumas vezes é necessário executar algum processo, stored procedure assim que o banco de dados for iniciado. O exemplo abaixo eu criei para me enviar um e-mail, caso o banco seja reiniciado sem eu saber.

Primeiro eu crio o processo que eu quero que seja executado em uma stored procedure. Ela tem que ser criada no Master.

use master
GO

CREATE PROCEDURE sp_AlertStartDBAS
BEGIN
Declare @sbody as VarChar(4000)

Declare @ssubject as VarChar(4000)

set @sbody = 'O servidor ' + CONVERT(sysname, SERVERPROPERTY('servername')) + ' foi inicializado às ' + Cast(getdate() as VarChar)
set @ssubject = 'Inicialização de Banco de Dados: ' + CONVERT(sysname, SERVERPROPERTY('servername'))

EXEC msdb.dbo.sp_send_dbmail @profile_name = 'Admin', @recipients = 'seuemail@provedor.com.br', @body = @sbody, @subject = @ssubject;

END



O próximo passo é configurar ela para ser executada no Start usando a proc sp_procoption. ( http://msdn.microsoft.com/en-us/library/ms181720.aspx )

EXECUTE sp_procoption @procname = 'sp_AlertStartDB', @optionname = 'startup', @optionvalue = 'on'

Tamanho físico dos arquivos do banco

Quer saber como está o tamanho fisico dos arquivos do seu banco?


SELECT Name,
[Physical_Name],
Left(CONVERT(VARCHAR, CAST( [SIZE]*8 AS MONEY ), 1),
Len(CONVERT(VARCHAR, CAST( [SIZE]*8 AS MONEY ), 1)) - 3) as Size_KB
FROM sys.database_files

Foreing Key sem Indices

Na maioria das vezes se você tem uma FK em uma tabela, a chance de se fazer buscas por essa chave é enorme, então, com bom senso, é bom ter indices nessas FKs.

Como descobrir quais colunas não possuem índices nas chaves estrangeiras (Foreing Key)

Select Object_Name(constid), Object_Name(fkeyid), Object_Name(rkeyid), * From Sysreferences Where not exists (Select 1 from Sysindexkeys Where id = fkeyid and fkey1 = colid and keyno=1)

Inicio da Atividades.

Além do objetivo principal que é o compartilhamento de conhecimento, o objetivo deste blog será tambem organizar meus códigos, informações, exemplos, conhecimento, etc sobre Banco de Dados MS-SqlServer.

Tenho muito coisa para publicar, então com calma vou passando para o Blog.