Arquivo
Série Mentes Brilhantes – Parte 3
O que fazer quando você não gosta do SQL Server e dos desenvolvedores que irão dar manutenção no seu código?
Crie uma consulta simples pra eles
DECLARE @Col1 Integer,
@Col2 Integer,
@Col3 Integer;
SET @Col1 = <Input1>;
SET @Col2 = <Input2>;
SET @Col3 = <Input3>;
IF (@Col3 = 0)
BEGIN
SELECT Tab1.Col1,
Tab1.Col2,
Tab1.Col3,
Tab1.Col7,
Tab1.Col8,
Tab1.Col6,
Tab1.Col9,
Tab1.Col10,
CASE WHEN ((Tab2.Col11 IS NOT NULL) AND
(Tab3.Col12 = ‘P’) AND
(Tab3.Col13 = ‘G’))
THEN CASE WHEN (Tab2.Col11 = ‘LL’)
THEN (SELECT CASE WHEN (Tab4.Col15 = ‘Q’)
THEN Tab4.Col16
ELSE Tab4.Col17
END
FROM Tab4(NOLOCK)
WHERE Tab4.Col1 = Tab1.Col1 AND
Tab4.Col2 = Tab1.Col2 AND
Tab4.Col3 = Tab1.Col3 AND
Tab4.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
ELSE CASE WHEN (Tab2.Col11 = ‘LF’)
THEN (SELECT CASE WHEN (Tab4_Fixos.Col15 = ‘Q’)
THEN Tab4_Fixos.Col16
ELSE Tab4_Fixos.Col17
END
FROM Tab4_Fixos(NOLOCK)
WHERE Tab4_Fixos.Col1 = Tab1.Col1 AND
Tab4_Fixos.Col2 = Tab1.Col2 AND
Tab4_Fixos.Col3 = Tab1.Col3 AND
Tab4_Fixos.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
ELSE CASE WHEN (Tab2.Col11 = ‘2F’)
THEN (SELECT CASE WHEN (Tab6.Col15 = ‘Q’)
THEN Tab6.Col16
ELSE Tab6.Col17
END
FROM Tab6(NOLOCK)
WHERE Tab6.Col1 = Tab1.Col1 AND
Tab6.Col2 = Tab1.Col2 AND
Tab6.Col3 = Tab1.Col3 AND
Tab6.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
ELSE (SELECT CASE WHEN (Tab4_Lim.Col15 = ‘Q’)
THEN Tab4_Lim.Col16
ELSE Tab4_Lim.Col17
END
FROM Tab4_Lim(NOLOCK)
WHERE Tab4_Lim.Col1 = Tab1.Col1 AND
Tab4_Lim.Col2 = Tab1.Col2 AND
Tab4_Lim.Col3 = Tab1.Col3 AND
Tab4_Lim.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
END
END
END
ELSE Tab1.Col18
END AS Col19,
Tab1.Col18,
Tab1.Col20,
Tab7.Col21,
Tab8.Col22,
Tab3.Col23,
Tab1.Col24,
CASE WHEN (Tab2.Col11 = ‘LL’)
THEN (SELECT Tab4.Col15
FROM Tab4(NOLOCK)
WHERE Tab4.Col1 = Tab1.Col1 AND
Tab4.Col2 = Tab1.Col2 AND
Tab4.Col3 = Tab1.Col3 AND
Tab4.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
WHEN (Tab2.Col11 = ‘LI’)
THEN (SELECT Tab4_Lim.Col15
FROM Tab4_Lim(NOLOCK)
WHERE Tab4_Lim.Col1 = Tab1.Col1 AND
Tab4_Lim.Col2 = Tab1.Col2 AND
Tab4_Lim.Col3 = Tab1.Col3 AND
Tab4_Lim.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6))
WHEN (Tab2.Col11 = ‘LF’)
THEN ‘F’
WHEN (Tab2.Col11 = ‘2F’)
THEN ‘2’
END AS Col15,
Tab1.Col25
FROM Tab1(NOLOCK)
INNER JOIN Tab7(NOLOCK)
ON Tab7.Col26 = Tab1.Col26 AND
Tab7.Tipo = Tab1.Tipo
INNER JOIN Tab8(NOLOCK)
ON Tab8.Col7 = Tab1.Col7
INNER JOIN Tab3(NOLOCK)
ON Tab1.Col1 = Tab3.Col1
RIGHT OUTER JOIN Tab2(NOLOCK)
ON Tab2.Col1 = Tab1.Col1 AND
Tab2.Col2 = Tab1.Col2 AND
Tab2.Col3 = Tab1.Col3 AND
Tab2.Col4 IN(SELECT Col4
FROM Tab5(NOLOCK)
WHERE Tab5.Col1 = Tab1.Col1 AND
Tab5.Col5 = Tab1.Col6)
WHERE Tab1.Col1 = @Col1 AND
Tab1.Col2 = @Col2 AND
Tab1.Col3 = @Col3 AND
Tab1.Col10 > 0 AND
Tab1.Col8 IS NOT NULL
Série Mentes Brilhantes – Parte 2
O que fazer quando você não gosta da clausula WHERE?
Use a clausula HAVING
SELECT Cliente, Vendedor
FROM Pedido
GROUP BY Cliente, Vendedor, Data
HAVING Data BETWEEN ‘20090101’ AND ‘20090201’
Série Mentes Brilhantes – Parte 1
Vou começar a publicar uma série que chamei de “Mentes Brilhantes”… espero que gostem 🙂 ….
O que fazer quando você precisa que o resultado de uma consulta não retorne linhas duplicadas?
Use o DISTINCT
SELECT DISTINCT Cliente, Vendedor
FROM Pedido
O que fazer quando você precisa que o resultado de uma consulta não retorne nenhuma linha duplicada?
Use o GROUP BY
SELECT Cliente, Vendedor
FROM Pedido
GROUP BY Cliente, Vendedor
O que fazer quando você precisa que o resultado de uma consulta não retorne nenhuma linha duplicada meeeesmo?
Use DISTINCT + GROUP BY. J
SELECT DISTINCT Cliente, Vendedor
FROM Pedido
GROUP BY Cliente, Vendedor
Cuidado com Views…
Pessoal já faz um tempo que estou sem blogar né?… Bom mesmo sem escrever para o blog por um tempo, isso não significa que fiquei sem escrever sobre SQL… na verdade bastante coisa aconteceu desde meu último post. Vamos as novidades:
· Tenho escrito bastante para o Simple Talk, a cada semana escrevo sobre um operador dos planos de execução, se você ainda não viu, segue o link para os artigos http://www.simple-talk.com/author/fabiano-amorim/… Ainda tem muito pela frente e eu estou adorando J, espero que eu consiga manter a meta de 1 por semana.
· O tão falado Marília Tech Day foi realmente um sucesso, foi uma honra pra mim poder participar e logo estarei no Prudente TechDay…
Tenho as gravações de minhas palestras e a palestra do Fabricio Catae. Eu fiz duas sessões uma sobre Joins no SQL Server e outra sobre índexação. Acabei sendo pego de surpresa pois o nosso amigo Rodrigo Fernandes teve um imprevisto e não conseguiu pegar o Vôo para o evento, então aproveitei o horário dele e falei sobre Índices no SQL Server, eu achei que ficou legal com bastante exemplos e boas perguntas da galera.
Ainda estou brigando para achar um programa decente para edição dos vídeos, assim que eu conseguir vou subir pro YouTube e coloco os links aqui no blog.
· Eu já havia sido convidado para falar sobre SQL Server em uma faculdade daqui de Tupã, então logo após o Marília TechDay falei sobre Certificações e SQL Server 2008 aqui na FACAT. Foi bem legal, é sempre bom ver a galera entusiasmada com as funcionalidades do SQL J.
Bom acabando a conversa fiada, vamos para o que interessa mais SQL Server.
Eu sempre me surpreendo com as situações que encontro no dia a dia… Hoje eu estava trabalhando em uma procedure que lia alguns dados a partir de uma view, e a consulta não utilizava um determinado índice de jeito nenhum. Fui olhar com calma e encontrei o seguinte cenário.
Vamos criar umas tabelas de teste para entender o problema.
IF OBJECT_ID(‘Pedidos’) IS NOT NULL
BEGIN
DROP TABLE Clientes
DROP TABLE Pedidos
END
GO
CREATE TABLE Pedidos(ID_Pedido Integer Identity(1,1),
ID_Cliente Int,
Data_Pedido DateTime,
Valor Numeric(18,2),
CONSTRAINT xpk_Pedidos PRIMARY KEY (ID_Pedido))
GO
CREATE TABLE Clientes(ID_Cliente Integer Identity(1,1),
Nome_Cliente VarChar(300)
CONSTRAINT xpk_Clientes PRIMARY KEY (ID_Cliente))
GO
CREATE INDEX ix_teste ON Clientes(Nome_Cliente)
GO
DECLARE @i Integer
SET @i = 0
WHILE @i < 10000
BEGIN
INSERT INTO Pedidos(ID_Cliente,
Data_Pedido,
Valor)
VALUES(ABS(CheckSum(NEWID()) / 10000000),
GetDate() – ABS(CheckSum(NEWID()) / 10000000),
ABS(CheckSum(NEWID()) / 1000000))
INSERT INTO Clientes(Nome_Cliente)
VALUES (NEWID())
SET @i = @i + 1
END
GO
SELECT * FROM Pedidos
SELECT * FROM Clientes
Segue um exemplo dos dados incluidos
Tenho uma tabela simples de pedidos e os dados de clientes, agora vou criar uma view que me retorna os dados dos pedidos com o nome do cliente.
CREATE VIEW vw_Teste
AS SELECT Pedidos.*, ISNULL(Clientes.Nome_Cliente,’Não Existe’) AS Nome_Cliente
FROM Pedidos
LEFT OUTER JOIN Clientes
ON Clientes.ID_Cliente = Pedidos.ID_Cliente
Sim, eu fiz o LEFT OUTER JOIN de propósito pois pode acontecer de um registro da Pedidos.ID_Cliente não estar na tabela de Clientes. Estou apenas usando um cenário para exemplo.
Existe um índice por Clientes.Nome_Cliente, vamos ver ele na prática. A consulta abaixo utiliza o mesmo select da view porém desta vez estamos fazendo um filtro por nome_cliente.
SELECT Pedidos.*, ISNULL(Clientes.Nome_Cliente,’Não Existe’) AS Nome_Cliente
FROM Pedidos
LEFT OUTER JOIN Clientes
ON Clientes.ID_Cliente = Pedidos.ID_Cliente
WHERE Clientes.Nome_Cliente like ‘A%’
Como podemos ver, o plano acima utilizou o índice ix_teste para filtrar os dados
Porem se efetuarmos o mesmo select utilizando a view temos o seguinte plano.
SELECT * FROM vw_Teste
WHERE Nome_Cliente like ‘A%’
Bem, a esta altura você já deve ter identificado qual é o problema. Mas pra quem ainda não pegou, fica a pergunta, porque o SQL faz o seek quando uso o código da view mas quando uso a view ele não faz o seek?
O que acontece é que na view estamos alterando o valor da coluna Nome_Cliente, repare que estou utilizando a function ISNULL para validar se os dados foram retornados, e isso impede que o SQL utilize o índice.
Para resolver este problema, poderiamos retornar duas colunas na view uma com o ISNULL e outra sem o ISNULL efetuar nenhuma alteração nela. Outra forma seria incluir outra coluna que faz parte da tabela Clientes no resultado da view e utilizar esta coluna.
Bom fica a dica.
Abraços
Novo artigo Simple Talk
Galera, saiu outro artigo que escrito pro ST,
Acessem lá…
http://www.simple-talk.com/sql/sql-tools/sql-server-intellisense-vs.-red-gate-sql-prompt/
Valeu
Fabiano vs DeadLock Paralelism
Galera, espero que este post sirva como ajuda para quem vai para o google a procura de uma resposta…
Pergunta, pode ocorrer um erro de deadlock em um select ?
Resposta: Sim. Um deadklock ocorre quando uma sessão esta esperando para acessar um determinado recurso que esta sendo utilizado por outra sessão. Estes recursos podem ser locks, paralelismo, sessões em espera, ou uma combinação destes itens.
Meu problema foi justamente este de paralelismo, eu tenho uma consulta que estava utilizando paralelismo e as vezes ela apresentava o seguinte erro:
Transaction (Process ID 55) was deadlocked on thread | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Repare no item em negrito e veja que o recurso que esta sendo acessado é esse tal de “communication buffer” que é o que? Paralelismo.
Resumo da ópera, como não sou DBA do ambiente e não posso mexer e nem ver a cor do servidor onde o SQL está instalado, tive que apelar para um OPTION(MAXPDOP 1) na minha proc. Desta forma evito o paralelismo e por consequencia o deadlock.
Abraços
sys.fn_PhysLocFormatter(%%physloc%%)
Sensacional a dica do Paul Randal sobre uma nova function não documentada… quem já usou o DBCC Page para ver os dados gravados nas páginas sabe do que estou falando.
Pena que só funciona pro SQL2008.
Abraço
TOP 100
Aqui estou, de volta a rotina, no ônibus a caminho de São Paulo… tempo para ler e escrever um pouco. Por falar em ler vou aproveitar para deixar uma dica ai pra vocês, caso gostem de leitura não deixem de aproveitar a oportunidade de comprar os livros da Série Deixados para Trás no Submarino, a série toda (13 livros) esta em promoção, não, não ganho nada com do Submarino, mas quem gosta de leitura tem que aproveitar… os livros são muito bons, eu li até o oitavo, agora comprei a série toda vou começar a ler tudo denovo… vale muito a pena.
Anyway, vamos logo ao assunto que interessa pra vocês SQL Server.
Entendendo o Problema
Quero compartilhar com vocês um problema bem interessante em uma consulta utilizando TOP e ORDER BY para tabelas com muitos registros.
O problema é o seguinte: Tenho uma consulta que faz um TOP em uma tabela utilizando a clausula ORDER BY por uma coluna diferente da chave cluster. Quando uso TOP 100 o SQL utiliza 550mb de memória, 9875 de CPU e retorna a consulta em 40 segundos, quando uso TOP 101 o SQL utiliza 798mb de memória, 42745 de CPU e retorna a consulta em 1 minuto e 20 segundos.
A pergunta que não quer calar, porque a inclusão de apenas 1 registro degrada tanto o desempenho?
TOP <= 100 VS TOP > 100
Bom, vamos entender como eu cheguei à conclusão abaixo.
O Operador TOP aplica um SORT para limitar a quantidade de linhas de duas formas, uma utilizando um algoritmo otimizado para ordenar poucas linhas, e outro para ordenar muitas linhas que é o operador de sort normal que conhecemos.
Suponha uma consulta SELECT TOP <N> * FROM TABELA, caso o <N> for >= 100 então o QO utiliza o algoritmo otimizado se o TOP N for > que 100 então ele utiliza o SORT no tempdb. Li esta explicação neste link no connect.
Configuração Ambiente
Entendendo isso, fui realizar os testes para medir a performance, para fazer isso medi o uso CPU, Memória e Tempo execução das consultas. Utilizei o Profiler para ler o uso de CPU e o Perfom para ler o contador “Total Server Memory”. A cada execução das consultas eu parava e iniciava o banco de dados para obter os valores de uso de memória.
Estou utilizando o SQL Server 2008 versão developer com Service Pack 1, e ele esta configurado para utilizar até 2 gb de memória. Segue a tela de configuração:
Como vocês podem observar nos prints abaixo, criei duas consultas. Ambas fazendo um select com TOP em uma tabela do meu banco de dados, a diferença entre as consultas é justamente o TOP 100 para o TOP 101.
Veja que para o primeiro plano de execução(TOP 100) o SQL Server reservou 1024 kb de memória, e para no segundo plano(TOP 101) ele reservou 224920 kbs de memória.
Veja o gráfico de memória utilizada pelo SQL Server ao rodar a primeira consulta. Repare que o SQL utilizou 553mb de memória e rodou a consulta em 44 segundos(veja no SSMS atrás do PerfMon).
Agora veja a diferença para a segunda consulta com o TOP 101. Bem diferente não?
Bom galera fica ai a dica… Vale a pena ficar de olho nos TOPs que retornem mais de 100 registros…
Ok ok, eu sei que faltou um item aqui de Conclusão, masss estou cansado, vou tentar dormir um pouco, depois quem sabe não escrevo mais sobre isso?…
Abraços
Datas, Backups
Pessoal, post rápido…
Ontem tivemos um problema no banco de dados que estamos utilizando em ambiente de desenvolvimento. A empresa que fez e nos disponibilizou o backup do baco de dados, jurava que o backup havia sido feito no dia 09/01/2010 as 3 horas da manhã.
Bom para verificar quando o backup havia sido finalizado foi fácil, bastou acessar o servidor e verificar a coluna backup_finish_date da msdb.dbo.backupset. Lá identifiquei que o backup havia terminado as 00:24 e não as 03 como estavam falando.
Fica ai a dica, caso um dia precisem analisar quando um backup foi feito, existem algumas tabelas no banco msdb que gravam varias informações sobre os backups.
Abraço
















