terça-feira, 23 de fevereiro de 2010

Artigo: Como o Query Optimizer utiliza (ou não) constraints unique e filtered indexes

Bom dia pessoal.

Aproveitando o gancho do último post, escrevi hoje um artigo com duas intenções:


  1. Mostrar como o query optimizer do SQL Server se beneficia de constraints unique
  2. Mostrar uma característica do optimizer com filtered indexes que me parece um bug
Como o artigo ficou grande e acho que a leitura será mais agradável com a formatação do PDF, não vou colocar ele inteiro como um post, deixo somente o link para download do PDF.

Como o Query Optimizer utiliza (ou não) constraints unique e filtered indexes




Espero que vocês gostem e deixem aqui seus comentários...

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

sexta-feira, 19 de fevereiro de 2010

Colunas UNIQUE e NULLs

Se quiser baixar o PDF e o script que utilizei, clique aqui.

Durante o último treinamento do SQL Server 2008 Internals, tive mais uma vez o prazer de usufruir de uma das grandes vantagens de ser instrutor, que é aprender com os alunos, então compartilho com vocês.
Estávamos discutindo sobre a utilização do NULL e eu joguei na sala a pergunta: Como fazemos para manter a unicidade de uma coluna e ainda permitirmos diversos valores nulos?

(Pausa para respirar e pensar um pouquinho)

No SQL Server a unicidade dos valores em uma coluna é garantida através de índices marcados como UNIQUE (cluster ou não) e uma vez inserido um NULL, nenhum outro NULL pode ser adicionado a tabela, pois é um valor duplicado.
É interessante ver esse comportamento de igualdade de nulos em uma constraint, pois se testarmos a igualdade de um nulo através de uma consulta, veremos que NULL é diferente de NULL (ele é desconhecido).

SELECT 'Comparando'
WHERE 1 = 1

SELECT 'Comparando'
WHERE NULL = NULL
go

Então como você resolve esse problema?

- Uma abordagem seria trabalhar com triggers na tabela, garantindo a unicidade dos valores não nulos.
- Particularmente não gosto dessa abordagem, por prolongar a transação e, se necessário, efetuar um rollback da mesma.

- Poderíamos garantir a unicidade através da aplicação ou SPs, mas aí temos que garantir que ninguém vai conseguir inserir um registro "por fora".
- Essa é uma abordagem interessante por evita o rollback, mas a falta de controle e de informações para o query optimizer (como no primeiro caso) não é legal.

- Outra abordagem que mostro no treinamento, seria criarmos uma coluna computada que em combinação com a coluna original (que precisa garantir unicidade para não-nulos) deve ser única. Essa coluna computada condicionalmente recebe um valor único (o campo da PK, por exemplo) caso o campo original seja nulo ou recebe NULL caso ele não seja nulo. Dessa forma poderíamos criar uma constraint UNIQUE nas colunas original e calculada, garantindo assim a unicidade não-nula.
- Gosto dessa abordagem porque trabalhamos com constraints e não preciso confiar em terceiros para que a regra seja respeitada.


Entendeu a explicação da terceira solução? Bem, eu já li vinte vezes o que escrevi e não entendi nada, então segue um exemplo para exemplificar melhor o que eu disse. :-)

USE tempdb
go

-- Criando a tabela de teste
IF (OBJECT_ID('Funcionario') IS NOT NULL)
DROP TABLE Funcionario
go

CREATE TABLE Funcionario
(
Codigo INT IDENTITY NOT NULL,
Nome VARCHAR(200) NOT NULL,
CNPJ CHAR(14) NULL)
go

ALTER TABLE Funcionario
ADD CONSTRAINT UNQ_Funcionario_CNPJ
UNIQUE (CNPJ)
go

ALTER TABLE Funcionario
ADD CONSTRAINT PK_Funcionario
PRIMARY KEY (Codigo)
go

-- Inserts OK
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno', NULL)
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar', '000.000.000-00')
go

-- Ambos os INSERTs abaixo irão trazer problema por conta da constraint UNIQUE
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno 2', NULL)
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar 2', '000.000.000-00')
go

-- Reconstruindo a tabela com a coluna computada
IF (OBJECT_ID('Funcionario') IS NOT NULL)
DROP TABLE Funcionario
go

CREATE TABLE Funcionario
(
Codigo INT IDENTITY NOT NULL,
Nome VARCHAR(200) NOT NULL,
CNPJ CHAR(14) NULL,
CNPJNulo AS (CASE WHEN CNPJ IS NULL THEN Codigo ELSE -1 END)
)
go

ALTER TABLE Funcionario
ADD CONSTRAINT PK_Funcionario
PRIMARY KEY (Codigo)
go

ALTER TABLE Funcionario
ADD CONSTRAINT UNQ_Funcionario_CNPJ
UNIQUE (CNPJ, CNPJNulo)
go

-- Inserts OK
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno', NULL)
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar', '000.000.000-00')
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Caio do Botafogo', '000.000.000-01')
go

-- Vai funcionar
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno 2', NULL)
go

-- Não vai funcionar
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar 2', '000.000.000-00')
go


Melhorou?
Dessa forma conseguimos garantir a unicidade antes que o valor seja inserido na tabela, sem a necessidade de criação de triggers.


SQL Server 2008

Agora que vem a sacada, enquanto estava falando sobre isso o amigo Burgos me perguntou: eu não conseguiria resolver esse problema utilizando índices com filtro?

(Momento de silêncio na sala)

Caramba! Se a unicidade é garantida através de índices e eu posso criar um índice com o predicado "IS NOT NULL", então provavelmente filtered index deve resolver o problema! Testamos e bang! Na mosca.

Eu tinha ficado tão focado nos ganhos de desempenho e tamanho dos índices com filtro que nunca tinha parado para pensar nessa utilização! Nada melhor do que dar aula e aprender também = Doscendo discimus.

Vamos ao exemplo…

-- Somente para SQL Server 2008
-- Resolução com filtered indexes
IF (OBJECT_ID('Funcionario') IS NOT NULL)
DROP TABLE Funcionario
go

CREATE TABLE Funcionario
(
Codigo INT IDENTITY NOT NULL,
Nome VARCHAR(200) NOT NULL,
CNPJ CHAR(14) NULL)
go

ALTER TABLE Funcionario
ADD CONSTRAINT PK_Funcionario
PRIMARY KEY (Codigo)
go

CREATE UNIQUE NONCLUSTERED INDEX idx_CNPF
ON Funcionario (CNPJ)
WHERE CNPJ IS NOT NULL
go

-- Inserts OK
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno', NULL)
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar', '000.000.000-00')
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Caio do Botafogo', '000.000.000-01')
go

-- Vai funcionar
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Ronaldo Fenômeno 2', NULL)
go

-- Não vai funcionar
INSERT INTO Funcionario (Nome, CNPJ) VALUES ('Nilmar 2', '000.000.000-00')
go


Viu que realmente funciona?!
Espero que a solução pré-SQL Server 2008 e a nova abordagem possam ajudar você no dia-a-dia.

Só fiquei agoniado com uma coisa nessa abordagem, relacionado com o Query Optimizer, mas vou fazer alguns testes e depois coloco aqui minhas considerações.

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda - www.srnimbus.com.br
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

quarta-feira, 17 de fevereiro de 2010

Denali será o SQL Server 2011?

Hoje cedo cruzei com uma informação dizendo que "Denali" será o codinome para o SQL Server 2011, através da Mary-Jo Foley (http://blogs.zdnet.com/microsoft/?p=5288), fonte que já usei outras vezes para descobrir novidades da Microsoft.

Para garantir eu busquei mais informações sobre o Denali e as achei, http://redmondmag.com/articles/2010/02/16/microsoft-to-release-sql-server-sps.aspx e http://news.softpedia.com/news/Introducing-Microsoft-Codename-Denali-the-Great-One-135006.shtml.

O curioso é que as fontes originais apontadas pelos artigos não mostram mais o nome Denali, inclusive um post do Dan Jones (http://blogs.msdn.com/dtjones/default.aspx) não está mais lá, pois um dos links aponta para um post do dia 14/02 e o último que vemos em seu blog é de 08/02.

Será que alguém anunciou o nome antes da hora? Eu sei que o MVP Summit está acontecendo agora lá em Seattle e me parece uma boa hora para começar a falar do Denali, então provavelmente os MVPs estão ouvindo sobre o SQL Server 2011 e sob NDA... Sim, estou com inveja. Quero saber detalhes e planos! :-)

Bom, fica aqui a dúvida de que o codinome do próximo SQL Server é mesmo Denali, mas pode ter certeza que eu estarei acompanhando de perto.

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

quinta-feira, 11 de fevereiro de 2010

Microsoft Connect e outras fontes

Bom, hoje em dia informação não falta, resta a nós pobres mortais, escolhermos o que é bom ou ruim e como você vai "gastar" seu tempo.

Ultimamente eu tenho me divertido com alguma threads e feedbacks do SQL Server que são colocados no espaço do SQL Server no Connect (http://connect.microsoft.com/). Connect para quem não sabe, é um canal da Microsoft com o público para discutir novas tecnologias e produtos (como o SQL Server), onde você pode reportar possíveis bugs, deixar seu feedbacks e até pedir a inclusão de novas funcionalidades no produto.

Como é um lugar onde encontramos muita gente envolvida com o produto no dia-a-dia, eu descobri diversas threads que merecem ser lidas e com comentários valiosos. Por exemplo: https://connect.microsoft.com/SQLServer/feedback/details/293188/amount-of-ram-for-procedure-cache-should-be-configurable.

Além do SQL Server temos info do Azure, SQL Azure, Oslo, AppFabric, etc. E, o que muito me agradou, ontem foi anunciado o espaço no Connect para a plataforma de dados da Microsoft! https://connect.microsoft.com/data. Quer deixar seu comentário sobre o Entity, Data Services e outros? Bom, já sabe o lugar.

Também lendo outros posts, descobri um guia de preparação para quem está pensando no MCM de SQL Server. Sinceramente, mesmo se você não têm o *menor* interesse no MCM, deve carregar o guia debaixo do braço e ler tudo! Aqui está o GUIA.

Cruzei também com o http://sqlserverpedia.com/, que me pareceu um lugar interessante para um passeio virtual.

Voltei a ler os grupos privados de MCT lá dos EUA. Mesmo com o inconveniente do NNTP e configuração do live mail, achei informações bem legais.

Post rápido...

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

Calendário de treinamentos Sr. Nimbus

Olá pessoal.

Passei os últimos dias refinando os nossos treinamentos de SQL Server e escrevendo novas ementas, para flexibilizar um pouco os cursos que temos e atender a demandas do mercado que venho recebendo. Infelizmente ainda não consigo abordar todas as ferramentas disponíveis no SQL Server (ex.: Reporting Services, Analysis Services, Service Broker, etc.), mas estamos trabalhando para isso, e você não perde por esperar!


Para evitar publicarmos esse tipo de informação somente por aqui (afinal ninguém merece pegar ementas pelo blog), montamos uma estrutura simples para anunciar os próximos treinamentos, até nosso Sharepoint público entrar no ar.

Acesse http://www.srnimbus.com.br/Treinamento.htm e veja o calendário do mês de Março, onde ficaremos em Brasília com os treinamentos SQL01, SQL02 e SQL05.

Atualmente já temos 8 treinamentos (SQL01 a SQL08) relacionados ao SQL Server, alguns em fase de finalização. Quer dar uma olhada nas nossas ementas? Fique de olho no site que vamos publicá-las já já.

Uma das vantagens em ser o dono dos treinamentos é a possibilidade de adaptá-los e melhorá-los ao longo do tempo, sem ficarmos presos a modelos fechados. Então se você começar a reparar algumas diferenças nas ementas no futuro, fique tranquilo, somos nós trabalhando para melhorar a qualidade e adicionar informações novas e interessantes.

Também estamos mantendo uma lista de interessados nos treinamentos que oferecemos, então se você têm interesse em nos ouvir falando, não deixe de cadastrar seu nome com informações de contato através do e-mail contato@srnimbus.com.br. Lembrando que não estamos restritos a Brasília, por enquanto ficaremos somente no Brasil (hehehe), mas sabe lá o que o futuro nos reserva...



[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm