Pesquisar este blog

quarta-feira, 16 de julho de 2014

SET ANSI NULLS ON/OFF


1. O que é?
Uma configuração do banco de dados que diz se as comparações com valores nulos estarão (ON) ou não (OFF) de acordo com o padrão ISO. A recomendação por parte da Microsoft é que esta opção sempre esteja como ON, pois em versões futuras do SQL Server não existirá a possibilidade de desabilitar conforme mencionado no link (http://msdn.microsoft.com/pt-br/library/ms188048.aspx). 

2. Como faço para saber como está esta opção em minha janela de conexão?
DECLARE @ANSI_NULLS VARCHAR(3) = 'OFF';
IF ( (32 & @@OPTIONS) = 32 ) SET @ANSI_NULLS = 'ON';
SELECT @ANSI_NULLS AS ANSI_NULLS;

3.  Como altero esta configuração em meu banco de dados?
-Para alterar apenas de uma determinada sessão\conexão, utilize:
SET ANSI_NULLS ON\OFF

-Para alterar a configuração padrão do banco de dados, utilize:
ALTER DATABASE [database] SET ANSI_NULLS ON\OFF

-Objetos de banco de dados já criados, como por exemplo stored procedures, serão executadas conforme foi configurado em sua criação. Para consultar o padrão da criação de um determinado objeto utilize:
  select a.*  --coluna uses_ansi_nulls
    from sys.sql_modules a
    join sys.sysobjects b
      on a.object_id = b.id
   where b.name like '%NOME%'

4.  Como funciona? Quais as diferenças?
Quando ANSI NULLS estiver ON, comparações com = NULL e <> NULL nunca retornarão resultados, mesmo que exista valores iguais a nulo e mesmo que existam valores diferentes de nulo.
Se utilizado IS NULL e IS NOT NULL as coisas mudam. IS NULL e IS NOT NULL não são afetados pela configuração SET ANSI NULLS ON\OFF. Neste caso serão retornadas linhas caso exista valor nulo ou caso exista valor diferente de nulo.

Exemplo:

        if OBJECT_ID('dbo.t1') IS NOT NULL
          DROP TABLE dbo.t1
       GO
      
       if OBJECT_ID('dbo.t2') IS NOT NULL
          DROP TABLE dbo.t2         
       GO       

       CREATE TABLE dbo.t1 (a INT NULL);
       CREATE TABLE dbo.t2 (a INT NULL);

       INSERT INTO dbo.t1 values (NULL);
       INSERT INTO dbo.t1 values (0);
       INSERT INTO dbo.t1 values (1);
      
       INSERT INTO dbo.t2 values (NULL);
       INSERT INTO dbo.t2 values (0);
      
       GO

       DECLARE @varname int;
       SET @varname = NULL;

       SET ANSI_NULLS ON
      
       SELECT a FROM dbo.t1 WHERE a = @varname;   -- 0(zero) linhas
       SELECT a FROM dbo.t1  WHERE a <> @varname; -- 0(zero) linhas
      
       SELECT a FROM dbo.t1 WHERE a IN (SELECT a FROM dbo.t2)     -- 1(uma)linha
       SELECT a FROM dbo.t1 WHERE a NOT IN (SELECT a FROM dbo.t2) -- 0(zero)linha
      
       SELECT a FROM dbo.t1 WHERE a IS NULL;      -- 1(uma) linha
       SELECT a FROM dbo.t1 WHERE a IS NOT NULL;  -- 2(duas) linhas


Quando ANSI NULLS estiver OFF, comparações com = NULL e <> NULL retornaram linhas caso exista valor NULL ou caso exista valor diferente de NULL. Repito que IS NULL e IS NOT NULL não são afetados pela configuração SET ANSI NULLS ON\OFF.

Exemplo:

       if OBJECT_ID('dbo.t1') IS NOT NULL
          DROP TABLE dbo.t1
       GO
      
       if OBJECT_ID('dbo.t2') IS NOT NULL
          DROP TABLE dbo.t2        
       GO      

       CREATE TABLE dbo.t1 (a INT NULL);
       CREATE TABLE dbo.t2 (a INT NULL);

       INSERT INTO dbo.t1 values (NULL);
       INSERT INTO dbo.t1 values (0);
       INSERT INTO dbo.t1 values (1);
      
       INSERT INTO dbo.t2 values (NULL);
       INSERT INTO dbo.t2 values (0);
      
       GO

       DECLARE @varname int;
       SET @varname = NULL;

       SET ANSI_NULLS OFF
      
       SELECT a FROM dbo.t1 WHERE a = @varname;   -- 1(uma) linhas
       SELECT a FROM dbo.t1  WHERE a <> @varname; -- 2(duas) linhas
      
       SELECT a FROM dbo.t1 WHERE a IN (SELECT a FROM dbo.t2)     -- 2(duas)linhas
       SELECT a FROM dbo.t1 WHERE a NOT IN (SELECT a FROM dbo.t2) -- 1(uma)linha
      
       SELECT a FROM dbo.t1 WHERE a IS NULL;      -- 1(uma) linha
       SELECT a FROM dbo.t1 WHERE a IS NOT NULL;  -- 2(duas) linhas

Ou seja, pra garantir, sempre use IS NULL \ IS NOT NULL  ;)

terça-feira, 8 de julho de 2014

Chapter 2- Lesson 2: Working with Data Types and Built-in Functions

Pra quem está acompanhando em ordem os posts talvez tenha reparado que pulei a lição 1 do capítulo 2. É uma lição voltada principalmente para as particularidades dos comandos FROM e SELECT. Realmente não vi nada que fuja do conhecimento trivial de quem já tem um pouco de experiência em TSQL. Sendo assim, bola pra frente...

Agora, esta lição 2,é punk rock até os ossos. Acredito que vou precisar de mais de um post pra conseguir compartilhar tudo de interessante que este capítulo possui.

Vou começar pelo básico, porém fundamental que são as escolhas corretas de tipos de dados para um bom aproveitamento de espaço e processamento. Vou tentar ser o mais direto possível e abordar as diferenças dos tipos de dados e apresentar também os tipos de dados que vejo serem pouco utilizados porém seriam perfeitamente aplicáveis as principais aplicações "convencionais".

Tipos de dados literais.

Tipo Descrição
CHAR Tipo de dado de tamanho fixo. Exemplo, ao definir uma coluna de tabela ou uma variável como CHAR(10)e gravar apenas a letra A, os outros 9 caracteres são preenchidos com espaço em branco. Sendo assim, utilize este tipo de dados apenas quando os valores a serem gravados tenham o mesmo tamanho. Exemplo, CPF, CNPJ, UF e etc. Cada caracter ocupa 1 byte de espaço.
NCHAR Também de tamanho fixo, ou seja, segue a lógica descrita do tipo CHAR. A diferença então entre CHAR e NCHAR é que este último tem a função de gravar caracteres globais que não podem ser expressos em apenas 1 byte. Se seu sistema será multi idioma como por exemplo em mandarim, japonês entre outros, utilize este tipo de dados. Não esquecendo de seguir a lógica de utilizar somente para valores literais que terão o mesmo tamanho. Cada caracter ocupa 2 bytes de espaço
VARCHAR Este é o tipo mais conhecido. Diferente de CHAR possui tamanho variável. Exemplo, ao definir uma coluna de tabela ou variável como varchar(80), e gravar o nome Murilo, diferente do campo CHAR só será ocupado o espaço referente aos 6 caracteres informados.Existe a opção também de utilizar VARCHAR(MAX) que permite uma infinidade de dados (2^31) e veio para substituir o tipo de dados TEXT. Cada caracter ocupa 1 byte de espaço.
NVARCHAR Da mesma forma que o tipo de dados VARCHAR também é de tamanho variável, NVARCHAR também segue a idéia de multi-idioma mencionado no tipo NCHAR. A dica então é utilizar este tipo de dado para colunas descritivas que possam variar de tamanho e idioma. Existe a opção também de utilizar NVARCHAR(MAX) que permite uma infinidade de dados (2^31) e veio para substituir o tipo de dados NTEXT. Cada caracter ocupa 2 byte de espaço.

Tipos de dados numéricos.

Tipo Descrição
TINYINT Este é um tipo de dados que vejo ainda ser pouco usado porém é extremamente útil, aplicável e economiza muito espaço em tabela. Tinyint pode variar de 0 a 255 e ocupa belos 1 byte. 
SMALLINT Este também é um tipo de dado pouco usado e também é muito útil e aplicável. Smallint pode variar de -32.768 a 32.768 e ocupa 2 bytes.
INT Este é o mais conhecido e muitas vezes utilizado sem necessidade. Em muitas situações pode ser substituído por smallint ou tinyint.Integer pode variar de -2.147.483.648 a -2.147.483.647 e ocupa 4 bytes.
BIGINT Como o nome já diz, este é um "baita" integer. Pode ser útil em campos sequenciais em tabelas com grande volume de dados. Bigint pode variar de -9.223.372.036.854.775.808 até 9.223.372.036.854.775.807 e ocupa 8 bytes.
NUMERIC\DECIMAL Bom, aqui chegamos já aos números praticamente infinitos.Este tipo de dados serve para armazenar valores muito pequenos, ou muito grandes, depende de como você resolve utilizá-lo. Este tipo de dado por ter até 38 dígitos. Sendo que destes 38 você pode usar até 38 casas decimais, neste caso não sobraria nada para os inteiros, podendo ser no máximo 0.99999999999999999999999999999999999999 (numeric(38,38); 38 vezes o 9). Ou você poderia utilizar um valor de 38 dígitos sendo destes 38, 18 para casas decimais. Neste caso você poderia armazenar um valor como esse por exemplo 12345678901234567890.123456789012345678 (numeric(38,18). Ou seja, extremamente grande. Os bytes a serem ocupados por este tipo de dados vai variar de acordo com a quantidades de dígitos especificado na declaração . De 1 a 9 dígitos = 5 bytes; de 10 a 19 = 9 bytes; de 20 a 28 = 13 bytes; de 29 a 38 = 17 bytes
FLOAT\REAL Ainda falando dos tipos numéricos com capacidade quase infinita.Este tipo de dado na minha opinião é um dos mais "estranhos". Não é um tipo de dado numérico preciso, é flutuante. Pra ser bem sincero, tenho dificuldade de entender este conceito pois é algo totalmente matemático. De forma resumida eu entendo que serve para representar números extremamente grande ou extremamente pequeno, porém devido a uma adaptação para armazenar estes números de forma binária, que é como os computadores trabalham, hardwares de diferentes capacidades e tecnologias podem apresentar variações de apresentação deste tipo de dado. Ou seja, resultados de manipulações matemáticas podem apresentar resultados diferentes em computadores diferentes. Em um dos sites que pesquisei, encontrei algo como; "se você não tem certeza do motivo de estar utilizando float\real não o utilize". Sendo assim, para aplicações convencionais, opte pelo NUMERIC.
MONEY O nome já diz bastante coisa.Este tipo de dados possui 4 casas decimais e sua utilização é indicada para valores monetários. Pode variar de -922.337.203.685.477,5808 a 922.337.203.685.477,5807 e possui 8 bytes. Tenha certeza que o que você está definindo como money é realmente um valor monetário, pois caso venha a sofrer muitas manipulações matemáticas entre outros valores money, o resultado poderá ser desagradável, até porque você não divide R$5,00/R$5,00 e depois multiplica por outros R$10,00.

Tipos de dados data e hora.

Tipo Descrição
DATE Este é um tipo de dados que vejo ainda ser pouco usado porém é extremamente útil, aplicável e economiza espaço em tabela. Date armazena somente dia, mês e ano e ocupa 3 bytes.
TIME Também é um tipo de dados que vejo ainda ser pouco usado porém é extremamente útil, aplicável e economiza espaço em tabela. Time armazena hora, minuto segundo e pode armazenar até nanosegundo, exemplo 23:59:59.9999999. Dependendo da precisão o espaço ocupado pode variar de 3 a 5 bytes.
SMALLDATETIME É uma baita alternativa ao famoso e comum DATETIME. Neste tipo de dado é armazenado ano/mes/dia/hora/minutos/segundos, os milésimos não entram, o que o torna um tipo de dado bastante compatível com a maioria das necessidades dos sistemas "convencionais".Este tipo de dado ocupado 4bytes.
DATETIME O datetime dispensa grandes apresentações, é o tipo mais utilizado para gravar datas. Sua precisão é de milésimos de segundos, o que o torna bastante convencional para a maioria das implementações. Ocupa 8 bytes.
DATETIME2 Este aqui é um super datetime, pode chegar a precisão de nanosegundos se declarado como datetime2(7).Digamos que para aplicações mais específicas ou por exemplo para monitoramento de processamento de rotinas de software seja útil, caso contrário o bom e velho datetime da uma boa conta do recado.

domingo, 23 de fevereiro de 2014

Chapter 1 - Lesson 2: Understanding Logical Query Processing

Uma postagem rápida simples porém considero bastante interessante devido ao conteúdo teórico. Nesta lição o livro foca em explicar como o SQL processa a execução de uma query.

O negócio funciona mais ou menos assim:

Escrevermos querys nesta ordem, certo?

1. SELECT
2. FROM
3. WHERE
4. GROUP BY
5. HAVING
6. ORDER BY

Porém o processamento é feito nesta ordem:

1. FROM
2. WHERE
3. GROUP BY
4. HAVING
5. SELECT
6. ORDER BY

Cada etapa de processamento gera como saída uma tabela virtual que serve de entrada para a próxima etapa. Esta ordem de execução pode explicar algumas coisas para um olhar mais atento. Um exemplo é este abaixo:




Por que o SQL não consegue identificar a coluna ano_nascimento? Justamente pela ordem de processamento que descrevi, ao processar a cláusula WHERE o SQL não tem conhecimento das colunas no SELECT.

Outro item interessante é com relação a seguinte situação:




Diferente da grande parte das linguagens de programação o SQL não processa instrução da esquerda para a direta como lemos. O SQL conceitualmente processa toda a instrução de uma fase de processamento de uma só vez. Digo conceitualmente porque seria impossível executar duas coisas ao mesmo tempo. Sendo assim a coluna ano_nascimento pode não existir ao realizar o processamento da coluna geracao.

Então é isso, por hoje é só! Apesar de serem situações simples, que todo mundo sabe que é errado, achei interessante pois aqui explica-se "o por que".

terça-feira, 18 de fevereiro de 2014

Chapter 1 - Lesson 1: Understanding the Foundations of T-SQL

Vamos lá, eu gostei muito dessa primeira lição porque menciona bastante aspectos conceituais do uso de banco de dados. Muitas pessoas não são muito ligadas nisso, preferem saber qual o comando faz tal coisa e ponto, porém eu costumo ter esta curiosidade com relação ao por que as coisas são de tal forma e tudo mais. Nada contra quem procura um conhecimento mais direto, afinal em certas situações focar no que é preciso para realizar determinada entrega é algo realmente mais sensato.

Vou mencionar em itens o que mais me chamou atenção nesta primeira lição:

  • Apesar da prova ser da Microsoft, do livro ser da Microsoft e da tecnologia abordada ser também da Microsoft,  a boa prática é sempre utilizar comandos padrões SQL Standard. Exemplo, ao invés de usar convert, use cast, a não ser que você venha fazer uso do terceiro parâmetro da função convert.  Sim, isso é uma recomendação do livro.
  • Uma coisa que eu achei legal e vejo ser pouco comentado é esclarecer que banco de dados relacional não significa relacional por permitir juntar várias tabelas e criar foreign keys. O nome banco de dados relacional tem este nome pois foi construído com base em um conceito matemático chamado relation. O livro não faz uma referência profunda a estes conceitos, porém exemplifica que tabelas com suas colunas e linhas são uma tentativa de representar\implementar este conceito matemático. 
  • Considerando então que o banco de dados foi construído baseado em um conceito matemático, o modo de interagir com ele pede que tenhamos conhecimento de alguns itens básicos deste conceito. 
    1. Diferente de qualquer linguagem comum de programação, a linguagem SQL leva em consideração que você está trabalhando com um conjunto de dados e não apenas com um dado de cada vez. Ou seja, resolva a maioria dos seus problemas com SELECT e não com cursores pois o banco de dados foi feito principalmente para trabalhar com conjuntos de dados.
    2. Por definição do conceito, um conjunto de dados não deve possuir dados repetidos. O SQL não força esta situação, porém quando criamos uma tabela por exemplo, a recomendação é que esta sempre tenha uma chave primária. Trabalhar com dados únicos respeita a definição do conceito e por consequência o banco de dados trabalha de melhor forma.
    3. Outro item que por definição é desconsiderado pelo banco de dados é a ordem de apresentação dos dados. Ou seja, apresentar {A,B,C} é igual a {C,B,A}. Forçar a ordenação dos dados não vai de encontro ao conceito, por isso em muitas situações a ordenação de dados custa caro no processamento. Outra coisa com relação a ordenação de dados e que sempre gera confusão, caso não explícito a cláusula ORDER BY, o SQL não garante o retorno de dados em uma determinada ordem. Isso mesmo, nunca tem-se essa garantia.
    4. Todo atributo de um modelo relacional também deve ter um nome único, uma boa prática sempre é dar um nome único as colunas a serem retornadas em um SQL.
    5. Para finalizar, o último item abordado é com relação aos 3 possíveis retornos em uma comparação. Os 3 possíveis retornos são verdadeiro, falso e desconhecido. O desconhecido em SQL é representado pelo NULL. Exemplo, JOAO, MARIA e NULL. Se quiséssemos selecionar todos os nomes que não sejam iguais a JOAO, quais teríamos como retorno? O retorno seria apenas MARIA, pois o NULL representa o desconhecido, ou seja, não se pode afirmar que é igual nem diferente a JOAO. Das "dicas" aqui mencionadas, acredito que essa seja a mais importante. Fiquem ligados quando trabalharem com colunas que permitem marca nulo, você pode estar desconsiderando resultados que talvez não seja essa a sua verdadeira intenção.
  • O último tópico teórico de hoje é com relação a nomenclatura. O livro menciona que o termo correto para nos dirigirmos ao que normalmente chamamos de registro é na verdade linha. E o que chamamos de campo é na verdade coluna. Outro item é chamarmos o NULL de valor, o correto seria chamar de "NULL mark" que em uma tradução direta para o português seria "marca NULL" ou simplesmente chamar de NULL.
Nossa, era pra ser um post rápido, porém com tanto conteúdo teórico ficou difícil. Da próxima tentarei um post mais direto.

Até mais...

Antes de começar, quem sou eu? Qual meu objetivo?

Bom, primeiramente vou me apresentar até para você ter uma ideia do que poderá comentar em relação aos posts e talvez pedir alguma ajuda ou mesmo compartilhar conhecimento.

Meu nome é Murilo Carlos Cardoso eu moro em Joinville-SC e trabalho com SQL Server aproximadamente 8 anos, desde sua versão 2005. Atualmente trabalho em uma empresa de desenvolvimento de software de gestão empresarial chamada TOTVS na área de customização para cliente nos produtos Logix WMS¹, e SARA². No produto SARA onde eu atuo com mais frequência, é muito forte a utilização dos recursos de programação em SQL Server utilizando stored procedures, triggers, views, functions entre outros.

Estou criando este blog com o objetivo de me preparar para a prova 70-461-Querying Microsoft SQL Server 2012. Pretendo fazer pequenos posts que fujam do conhecimento trivial e abordem também aspectos teóricos e conceituais. Todos os posts serão relacionados ao livro da prova fornecido pela Microsoft e ilustrado abaixo. Para quem não conhece o funcionando das certificações Microsoft segue o link para obter mais informações -> http://www.microsoft.com/learning/pt-br/certification-overview.aspx



Espero que gostem!

WMS - Warehouse Management System
SARA - Sistema de Armazenamento para Recintos Alfandegados.