Treinamento para consultas com SQL Server - Parte 3 - Filtros usando a cláusula WHERE

www.freeimages.com

Os filtros são uma forma de restringir o número de registros que são retornados por uma consulta. Você DEVE SEMPRE USAR filtros. Mesmo que os seus projetos os bancos estejam muito pequenos. Em algum ponto do tempo isso irá mudar e um filtro bem feito pode fazer a diferença entre uma consulta executando com rapidez e outra que se torna lenta com o decorrer do tempo.

A forma mais comum de se implementar os filtros na linguagem SQL é através da cláusula WHERE onde a sintaxe básica é a seguinte:

SELECT [colunas]
FROM [tabelas]
JOIN [mais tabelas]
WHERE [condição1]
   AND | OR [condição2]
   ...
   AND | OR [condiçãon]

O WHERE é colocado sempre depois das tabelas e relacionamentos. Podem existir tantas condições quantas forem necessárias, mas, a única forma de conectar estas é através das palavras chave AND e OR e aqui está um ponto importante pois a maneira certa de usar estes dois operadores causa uma diferença muito grande no resultado final.

Dependendo da consulta desejada, da posição das condições e da presença ou não de parênteses os resultados podem ser totalmente diferentes. Vamos considerar as seguintes tabelas:

Recupere as colunas FirstName, LastName (PERSON.PERSON), AddressLine1 e City (PERSON.ADDRESS) para todos os registros em PERSON.PERSON em que a primeira letra da coluna FirstName seja “V” e que o último nome seja “Smith” ou que resida na cidade de “Hamburg” (Hamburgo).

Pelo enunciado podemos ter duas consultas. Na primeira são recuperados todos os registros para o primeiro nome começando com a letra “V” e qualquer uma das duas condições abaixo:

  1. O último nome seja “Smith”
  2. A cidade seja “Hamburg”

Neste caso, veja como fica a consulta:

select p.FirstName + ' ' + p.LastName as CompleteName
	,pa.AddressLine1
	,pa.city
from person.person p
join Person.BusinessEntityAddress BEA on 
	BEA.BusinessEntityID = p.BusinessEntityID
join person.address pa on pa.AddressID = bea.AddressID
where p.FirstName like 'v%' and p.LastName='smith' or pa.City='Hamburg'

O que traz o resultado abaixo:

Percebe algum problema? Na realidade a consulta está correta. Mas, talvez o resultado esperado fosse:

  1. Somente pessoas com a primeira letra do nome igual a “V” e uma das duas condições:
    • Sobrenome “Smith”
    • Cidade do endereço ser “Hamburg”

Por que todos os outros nomes foram trazidos?

Porque o avaliador de expressões do SQL Server interpretou a cláusula AND e trouxe todos os registros que correspondessem a condição e na sequência, a condição após OR. Conforme o princípio de avaliação de condições da disciplina de lógica:

TRUE AND TRUE = TRUE

TRUE OR TRUE = TRUE

Assim, temos três condições verdadeiras. A solução aqui requer duas etapas:

  1. Primeiro entender realmente o que se deseja, somente o primeiro nome com “V” que tenha o sobrenome “Smith” ou que resida em “Hamburg”
  2. A primeira letra do nome ser “V” e o sobrenome “Smith” ou qualquer letra no primeiro nome desde que resida na cidade “Hamburg” – que é como a primeira consulta foi montada.

Para o primeiro caso a consulta precisa ser alterada da seguinte maneira:

select p.FirstName + ' ' + p.LastName as CompleteName
	,pa.AddressLine1
	,pa.city
from person.person p
join Person.BusinessEntityAddress BEA on 
	BEA.BusinessEntityID = p.BusinessEntityID
join person.address pa on pa.AddressID = bea.AddressID
where p.FirstName like 'v%' and (p.LastName='smith' or pa.City='Hamburg')

Esta consulta produz resultados bem diferentes que podem ser conferidos abaixo.

Neste caso a presença dos parênteses fez com que o avaliador de expressões considerasse as duas condições dentro destes fosse avaliada completamente antes de dar o resultado.

Operadores

Os operadores padrão para a linguagem SQL são os seguintes:

Operador
Descrição
=
Igualdade
<> 
Diferente de, dependendo do produto de banco de dados, pode ser usado o operador “!=
> 
Maior do que
< 
Menor que
>=
Maior ou igual
<=
Menor ou igual
BETWEEN
Valores entre dois valores específicos
LIKE
Usa um padrão para busca
IN
Especifica múltiplos valores para uma mesma coluna

É importante observar:

  • Estes operadores são comuns em todos as implementações da linguagem de SQL, independentemente de qual banco de dados
  • Cada produto SGBD possui suas particularidades. Sempre consulte a documentação.
  • Os operadores específicos do SQL Server serão vistos adiante.

Existem algumas observações importantes para cada operador, vamos conferir alguma destas.

COLLATION do banco de dados SQL

Para o correto armazenamento dos dados no formato texto o SQL Server configura informações especiais sobre a forma como deve tratar o texto armazenado para cada banco de dados.

Estas configurações são chamadas de COLLATION. As mais comuns são:

  • SQL_Latin1_General_CP1_CI_AS. Indica que são usados caracteres de idiomas baseados no latim como o português. Que será CASE INSENSITIVE, ou seja, não serão consideradas letras maiúsculas ou minúsculas sendo tratadas como a mesma coisa e que o banco será ACCENT SENSITIVE, ou seja, fará diferença entre letras acentuadas e não acentuadas, na prática “está” será diferente de “esta” em consultas.
  • SQL_Latin1_General_CP1_CI_AI. Idem ao item anterior, porém, ACCENT INSENSITIVE, ou seja, não faz diferenciação entre letras acentuadas ou não.

É possível, ao executar uma consulta, forçar o tipo usado para comparação para evitar problemas usando a sintaxe abaixo:

WHERE [coluna string] = [expressão] COLLATE [collation] 

A indicação do COLLATION deve vir sempre sem aspas.

Para saber qual a configuração de COLLATION do banco de dados, no SSMS clique sobre o banco de dados com o botão direito e escolha Propriedades, na janela que se abre observe o campo “Agrupamento”.

Caractere especial “[]”

Para o SQL Server os caracteres “[]” são delimitadores de colunas. Assim, se for usar em consultas, é necessário realizar um tratamento caso a busca inclua estes caracteres ou o SQL Server interpretará o texto como nome de coluna. O jeito correto é passar o caractere de abertura “[“ delimitado da seguinte forma:

WHERE [coluna] = ‘[[]expressão dentro dos delimitadores]’

Operador IN

Normalmente é usado para evitar escrever OR com muitos valores e seu uso é muito comum para colunas que indicam status ou algum tipo de flag:

WHERE [COLUNA] = VALOR1 OR [COLUNA] = VALOR2 ... OR [COLUNA] = VALORn

Este tipo de consulta fica da seguinte forma:

WHERE [COLUNA] IN (VALOR1, VALOR2, ..., VALORn)

O interessante disto é que:

  • Qualquer tipo de dado pode ser usado na lista, desde que todos do mesmo tipo. Assim, pode se ter lista de números, strings, datas, etc.
  • É muito mais fácil de fazer a leitura
  • Pode-se usar sub consultas desde que esta retorne apenas uma coluna para a comparação

Para este último caso, considere as três tabelas do diagrama a seguir.

A tabela PRODUCTION.PRODUCT relaciona-se com a PRODUCTION.PRODUCTSUBCATEGORY através da coluna ProductSubcategoryID e esta, relaciona-se com PRODUCTION.PRODUCTCATEOGRY pela coluna ProductCategoryID.

Se for necessário fazer uma consulta para recuperar apenas os produtos da categoria (PRODUCTION.PRODUCTCATEGORY) “Bikes” devemos fazer o filtro na tabela PRODUCTION.PRODUCTCATEGORY e em seguida em PRODUCTION.PRODUCTSUBCATEGORY. A consulta ficará desta forma:

select p.name,p.ProductNumber
from Production.Product p
where p.ProductSubcategoryID in (
	select ps.ProductSubcategoryID
	from Production.ProductSubcategory ps
	where ps.ProductSubcategoryID = p.ProductSubcategoryID
		and ps.ProductCategoryID in (
			select pc.ProductCategoryID 
			from Production.ProductCategory pc
			where pc.Name = 'Bikes'
				and pc.ProductCategoryID = ps.ProductCategoryID
		)
	)

Neste caso a cláusula IN foi usada para filtrar os identificadores das subcategorias e também das categorias, assim, fica claro que podemos ter vários níveis deste operador em cascata. O resultado ficou como na figura a seguir.

Exists

Este operador é usado para verificar uma condição em um conjunto de linhas de uma tabela, por exemplo, retornar apenas os registros caso sejam encontrados pelo menos uma ocorrência em uma sub consulta.

Este é bem parecido com a cláusula IN usando sub consultas, mas, é diferente por não fazer comparações com colunas da tabela pai. O resultado de EXISTS é sempre boolean.

Considere novamente as tabelas do diagrama exibido na figura anterior.

Em vez de fazer duas sub consultas com IN pode se fazer a junção das tabelas PRODUCTION.PRODUCT e PRODUCTION.PRODUCTSUBCATEGORY e sobre esta aplicar a cláusula EXISTS como na instrução SQL abaixo.

select p.name,p.ProductNumber
from Production.Product p
join Production.ProductSubcategory ps on ps.productsubcategoryid="p.ProductSubcategoryID" 
where EXISTS (
	select *
	from Production.ProductCategory pc
	where pc.ProductCategoryID=ps.ProductCategoryID
		and pc.Name='Bikes'
	)

Esta consulta produz os resultados demonstrados na sequência.

Uma vantagem do uso deste operador é que pode ser usado para simplificar a instrução que foi escrita como, assim como a cláusula IN, ser usado com NOT, como no exemplo abaixo:
select p.name,p.ProductNumber
from Production.Product p
join Production.ProductSubcategory ps on
	ps.ProductSubcategoryID = p.ProductSubcategoryID
where NOT EXISTS (
	select *
	from Production.ProductCategory pc
	where pc.ProductCategoryID=ps.ProductCategoryID
		and pc.Name='Bikes'
	)

Palavra final

Este post mostra o básico do básico da cláusula WHERE, embora, tenha adicionado algumas informações adicionais acho interessante para quem quiser se aprofundar dar uma pesquisada nos links abaixo:

Um abraço e até a próxima!