Podemos utilizar os operadores de comparação para strings, da mesma maneira que para números. A ordem a ser seguida nesses casos é a alfabé- tica ou lexicográfica para cada posição da string. Assim, a palavra “casa”
será menor que “caso”, bem como menor que “casarão”. A ordem numé- rica também está associada à alfabética, de forma que a palavra “casa1” será menor que “casa2”.
Outra forma poderosa de comparação de strings é sobre o casamento de padrões entre elas. Numa comparação desse tipo, expressões que tenham a forma: “s LIKE p”, retornam strings que atendam um certo padrão, sendo s uma variável ou string e p o padrão de ocorrência. Os caracteres especiais “%” e “_” são utilizados para identificar os padrões procurados em um conjunto de strings. Por exemplo, caso queiramos obter todos os nomes da tabela “Clientes” que comecem com Carlos, é possível utilizarmos a expressão SQL:
SQL (17):
SELECT nome FROM clientes
WHERE nome LIKE “Carlos%”
O símbolo “%”, ao final, indica que serão considerados para a tabela de saída todos os nomes de clientes que comecem com Carlos. Caso quei- ramos todos os nomes que terminem com Silva, o símbolo “%” precisa estar no início do padrão de comparação:
SQL (18):
SELECT nome FROM clientes
WHERE nome LIKE “%Silva”
Ou, se quisermos encontrar uma palavra qualquer dentro de um campo, não importando a posição onde ela esteja, colocamos no início e no final do padrão:
SQL (19):
SELECT nome FROM clientes
109
No caso de compararmos com um padrão que case exatamente em ter- mos de caractere em vez de uma string, utilizamos o símbolo “_”. Assim, para uma consulta que deve retornar apenas os nomes que começam com a letra “L” e tenham apenas cinco letras, a obtemos com a consulta:
SQL (20):
SELECT nome FROM clientes
WHERE nome LIKE “L _ _ _ _“
Os caracteres para casamento de padrões podem variar entre certas implementações. Em Microsoft Access, por exemplo, em vez de “%” usa-se “∗” (asterisco), e em vez de “_” usa-se “?”.
Certos desenvolvedores web, quando utilizam sistemas de bus- cas em bancos de dados baseadas em SQL, na maior parte dos casos não se preocupam em interceptar a possibilidade do usu- ário colocar o caractere % no campo para busca. Uma busca que tenha a expressão campo LIKE ‘%’ retorna todos os registros que existem dentro da tabela para o atributo “campo”. Depen- dendo da quantidade de registros, o servidor de banco de dados pode ficar sobrecarregado com a pesquisa a ser efetuada.
A utilização de operadores de comparação para data e para hora tam- bém é válida, pois internamente os bancos de dados utilizam formatos numéricos para conversão de data e de hora.
[
trabalhando com data e com hora
]
Existe uma variedade de formatos de data e de hora que podem ser tra- balhados em bancos de dados. A maioria implementa o tratamento de
forma diferenciada, não seguindo o padrão. De acordo com o padrão SQL2, um valor constante de hora deve ser expresso com o prefixo DATE, antes da data e entre aspas, assim como:
DATE‘15/09/2004’
Para constantes referentes a hora, usamos o prefixo TIME:
TIME‘09:03:02’
Os bancos de dados implementam formas diferentes de tratar data e hora. O Microsoft Access exige que seja colocado cerquilha (#) em vez de aspas e que seja sem a palavra-chave DATE. Assim, deve ser utilizado #15/09/2004# e #09:03:02#. Bancos de dados como o Interbase e o MySQL não necessitam de prefixo para ambos, data e hora, desde que estejam nos formatos corretos.
[
ordenando os resultados
]
Uma consulta pode retornar os resultados ordenados de acordo com o tipo do atributo. Através do uso da cláusula ORDER BY, podemos colo- car um ou mais atributos pelos quais queremos que apareça a ordem da consulta efetuada. Por exemplo, para retornar a tabela de clientes orde- nada pelo nome do cliente:
SQL (21):
SELECT nome FROM clientes ORDER BY nome
Ou, ainda, pelo nome do cliente e telefone:
SQL (22):
SELECT nome FROM clientes
111
Se for atendido o critério de o nome do cliente começar com a letra “A”, temos:
SQL (23):
SELECT nome FROM clientes WHERE nome LIKE “A%” ORDER BY nome, telefone
Em vez de utilizarmos o nome do atributo, podemos também colocar o número de ordem, do mesmo, na tabela:
SQL (24):
SELECT nome FROM clientes WHERE nome LIKE “A%” ORDER BY 2,3
A ordenação pode ter sua seqüência invertida, ao colocarmos o quali- ficador DESC após o atributo. Assim a seguinte consulta apresenta os dados na ordem inversa de nomes:
SQL (25):
SELECT nome FROM clientes ORDER BY nome DESC
[
consultas aninhadas ou subconsultas
]
Veremos a partir daqui formas mais aprofundadas de utilização da cláu- sula WHERE. Quando falamos em comparações nas seções anteriores, utilizamos números, caracteres ou strings. No entanto é possível fazer a comparação também com resultados provenientes de outras consultas que estejam acontecendo dentro da principal. Denominamos a isso de
aninhamento de consultas ou subconsultas.
Recordando o exemplo, já visto para o produto cartesiano, onde buscá- vamos o nome do cliente para o pedido 1010:
SQL (26):
SELECT clientes.nome FROM pedido, clientes
WHERE pedido.codcli = clientes.codigo AND pedido.codigo = 1010
Observe que, nesse exemplo, a expressão pode ser modificada consi- derando-se o uso de uma subconsulta. Assim, deve ser verificado se o código do cliente é igual ao retornado pelo resultado da subconsulta à tabela “Pedido”: SQL (27): SELECT clientes.nome FROM clientes WHERE clientes.codigo = SELECT pedido.codcli FROM pedido WHERE pedido.codigo = 1010)
Observe que essa expressão retorna o mesmo resultado e que, na cláu- sula WHERE, a comparação é feita usando o campo clientes.codigo com o resultado que vem da subconsulta. Analisando, agora, a subcon- sulta, são projetados para o campo pedido.codcli os registros da tabela “Pedido” que atendam à condição de que o código do pedido seja igual a 1010. Nesse caso, para aquele registro que atender à condição de igualdade (=) da cláusula WHERE da consulta principal (campo codigo na tabela “Clientes” igual ao campo codcli na tabela “Pedido”), o nome do cliente é mostrado no resultado.
nome
Luís Alberto
Um fato a ser considerado é que a subconsulta pode retornar apenas um valor. É como se a consulta que gerasse esse resultado fosse:
SELECT cliente.nome FROM clientes
113
Será gerado um erro, caso exista mais de um registro no resultado da subconsulta. Assim, o operador de igualdade (=) deve ser substituído por outro tipo de operador, para que seja possível executar a consulta. Tais operadores serão vistos na próxima seção.
Outra observação pertinente a essa consulta aninhada deve ser feita. Note que o comando a seguir irá retornar apenas os campos presentes na tabela “Clientes”, devido ao uso do asterisco na cláusula SELECT e da referência à tabela “Clientes” na cláusula FROM da consulta principal:
SQL (28): SELECT * FROM clientes WHERE clientes.codigo = SELECT pedido.codcli FROM pedido WHERE pedido.codigo = 1010
E o comando a seguir retorna todos os campos presentes nas tabelas “Clientes” e “Pedido”, devido à presença das duas tabelas na cláusula
FROM. Portanto as duas formas de expressão não são perfeitamente
equivalentes:
SELECT *
FROM pedido, clientes
WHERE pedido.codcli = clientes.codigo AND pedido.codigo = 1010
[
condições em consultas aninhadas
]
Certos operadores – também chamados de predicados ou qualificado-
res – em SQL podem ser aplicados a uma tabela ou relação e produzirem um valor lógico. Uma subconsulta pode retornar tal valor para uma con- sulta principal. Os operadores disponíveis são: IN, EXISTS, ALL e ANY.
O operador IN
Vejamos a seguir um exemplo onde o resultado possa ser mais de um registro. Supondo que é preciso retornar os nomes de clientes com valor de pedido acima de R$1.000.00, fazemos:
SQL (29): SELECT clientes.nome FROM clientes WHERE clientes.codigo IN (SELECT pedido.codcli FROM pedido WHERE pedido.valor > 1.000,00) Resultando: nome Ernesto Luís Alberto
Note que foi substituído o operador de igualdade (=) pelo operador IN. Este operador atua na consulta indicando se o código do cliente está
contido no resultado da subconsulta. Dessa maneira, o operador IN opera de forma mais genérica que o operador de igualdade (=).
O operador EXISTS
O operador EXISTS P indica uma condição que é verdadeira se e somente
se P não for vazio. Entende-se aqui “P” como sendo o resultado (ou rela- ção) de uma subconsulta aninhada. Se “P” não possui nenhum registro, então EXISTS P retorna falso, caso contrário retorna verdadeiro.
O operador IN não deve ser interpretado como semelhante ao operador
EXISTS. Enquanto IN faz a pergunta: “está contido?”, o operador EXISTS,
115
A consulta a seguir modifica o exemplo dado para IN, utilizando agora
EXISTS: SQL (30): SELECT clientes.nome FROM clientes WHERE EXISTS (SELECT pedido.codcli FROM pedido WHERE pedido.valor > 1.000,00)
A interpretação agora é diferente: se existir algum pedido maior que
R$1.000.00, os nomes de clientes serão mostrados. Como existem dois registros, o conjunto da subconsulta não é vazio. Assim temos o resultado mostrando todos os nomes de clientes que estão na tabela “Clientes”.
nome Ernesto Amélia Luís Alberto José Antonio Carlos Silva
O operador lógico NOT pode ser aplicado aos operadores IN e EXISTS, de forma a apresentar o complemento da operação. Assim, caso a con- sulta utilizando IN seja:
SQL (31):
SELECT clientes.nome FROM clientes
WHERE clientes.codigo NOT IN (SELECT pedido.codcli FROM pedido
WHERE pedido.valor > 1.000,00)
O resultado responde à seguinte pergunta: “Quais os nomes de clientes cujos pedidos não são maiores que 1.000.00?”. Nesse caso, a resposta é:
nome
Amélia
José Antonio
Carlos Silva
No entando, se aplicarmos o operador NOT à consulta utilizada para o operador EXISTS,
SQL (32):
SELECT clientes.nome FROM clientes WHERE NOT EXISTS (SELECT pedido.codcli FROM pedido
WHERE pedido.valor > 1.000,00)
o resultado será vazio, pois a pergunta da consulta agora é: “Não exis- tem registros com valor de pedido maior que R$1.000,00?”. Como tais registros existem, a resposta para a pergunta retorna falso:
nome
O operador ALL
O operador ALL deve ser disposto na cláusula WHERE tal como campo > ALL subconsulta
retornando uma condição verdadeira se e somente se campo for maior que todo e qualquer valor localizado em subconsulta. O operador de comparação (>) pode ser trocado ainda por outros operadores (>=, <, <=, = ou <>). Por sua vez, o operador lógico NOT pode ser aplicado à frente, negando a condição. Assim, para selecionar os registros, na con- sulta principal, que venham a satisfazer a comparação com todos os registros retornados pela subconsulta, utilizamos o predicado ALL.
117
Como exemplo, montamos uma consulta com a cláusula ALL para retor- nar a informação do código e valor do pedido com maior subtotal exis- tente dentro da tabela “Itens”, mencionada anteriormente:
SQL (33):
SELECT pedido.codigo, pedido.valor FROM pedido
WHERE pedido.valor >= ALL (SELECT itens.subtotal FROM itens)
Com a seguinte relação como resultado:
codigo valor
1005 1.200,00
O operador ANY
O operador ANY deve ser disposto tal como campo > ANY subconsulta
retornando uma condição verdadeira se e somente se campo for maior que pelo menos um valor em subconsulta. O operador de comparação (>) pode ser trocado ainda por outros operadores (>=, <, <=, = ou <>). Sendo que o operador lógico NOT pode ser aplicado à frente, negando a condição. Assim, para selecionar os registros, na consulta principal, que venham a satisfazer a comparação com algum dos registros retornados pela subconsulta, utilizamos o predicado ANY (ou também SOME). Como exemplo, desejamos saber, dentro da tabela “Pedido”, quais são os que possuem itens cujo subtotal são 50% ou mais do valor do pedido. Então:
SQL (34):
SELECT pedido.codigo, pedido.valor FROM pedido
WHERE pedido.valor >= ANY (SELECT itens.subtotal FROM itens
WHERE itens.subtotal > pedido.valor * 0.5 AND itens.codped = pedido.codigo)
E a seguinte relação como resultado:
codigo valor
1005 1.200,00
1008 960,00
1015 755,00
Ou seja, retornaram aqueles pedidos e seus valores nos quais existe itens cujo subtotal seja maior ou igual a 50% do valor total do pedido. Note que é permitido usar expressões na comparação utilizando operações algébricas.
Algumas equivalências podem ser encontradas entre os operadores. Por exemplo
campo = ANY subconsulta campo
é igual a pelo menos um registro em subconsulta. E o mesmo que campo IN subconsulta campo
está contido em subconsulta. Assim como para o operador ALL campo <> ALL subconsulta campo
não é igual a todo e qualquer registro em subconsulta. É o mesmo que campo NOT IN subconsulta campo
119
[
eliminando registros duplicados
]
Em alguns resultados de consultas, pode ser que apareçam registros com valores duplicados. O uso da palavra-chave DISTINCT, presente na cláu- sula SELECT, faz com que seja apresentado apenas um dentre os regis- tros repetidos que possam aparecer.
Por exemplo, se quisermos obter os códigos de pedidos a partir da tabela “Itens”, lançamos a seguinte consulta:
SELECT codped FROM itens
Obtendo o seguinte resultado:
codped 1005 1008 1008 1010 1010 1010 1015 1015 1023 1023 1023
Para eliminar os registros que aparecem em duplicata, modificamos o comando SQL para:
SQL (35):
SELECT DISTINCT codped FROM itens
Obtendo somente um código para cada pedido. Assim: codped 1005 1008 1010 1015 1023
[
união
]
A operação de união em SQL é equivalente à existente para AR. A repre- sentação de união de duas tabelas “A” e “B” faz-se: A U B. Uma ressalva a ser feita é que o esquema das duas tabelas tem que ser igual, ou seja, conter os mesmos atributos.
Assim, se temos duas tabelas, “Computador” e “Notebook”, com os seguintes atributos:
COMPUTADOR(codigo, CPU, RAM, HD, CD, preco) NOTEBOOK(codigo, CPU, RAM, HD, Tela, preco)
A seguinte união em AR pode ser efetuada:
πcodigo,preco(Computador) U πcodigo,preco(Notebook)
Em SQL, utilizamos a cláusula UNION para efetuar a união. O comando SQL equivalente à consulta acima é:
SQL (36):
SELECT codigo, preco FROM computador UNION
SELECT codigo, preco FROM notebook
121
Veja que é preciso fazer dois comandos SQL e ligá-los pela cláusula
UNION. Caso existam registros repetidos, a operação de união irá auto-
maticamente eliminá-los. Porém, se for desejado o contrário, o qualifica- dor ALL pode ser usado para a operação apresentar todos os registros, inclusive os repetidos:
SQL (37):
SELECT codigo, preco FROM computador UNION ALL
SELECT codigo, preco FROM notebook
[
diferença
]
A operação de diferença, apesar de não ser implementada em alguns bancos de dados, também é equivalente à existente na AR. A representa- ção de diferença de duas tabelas “A” e “B” faz-se A − B*.
A seguinte diferença em AR, portanto, é possível de ser efetuada: πcodigo (clientes) − πcodcli (pedido)
Em SQL, utilizamos a cláusula EXCEPT (do padrão SQL) ou MINUS para efetuar a diferença. O comando SQL equivalente à consulta acima é:
SQL (38): SELECT codigo FROM clientes EXCEPT SELECT codcli FROM pedido ou
* A mesma ressalva feita à operação de união vale aqui, ou seja, que o esquema das duas tabelas seja igual, contenha os mesmos atributos.
SELECT codigo FROM clientes MINUS SELECT codcli FROM pedido Produzindo: codigo 295
Os bancos de dados PostgreSQL e DB2 utilizam o EXCEPT. Em Oracle é utilizado o MINUS. O Microsoft SQL Server, o Sybase, o Microsoft Access, o Interbase e o MySQL não implementam a operação de diferença, devendo a mesma ser simulada. O Post- greSQL e o DB2 também suportam o EXCEPT ALL.
Para simular um comando equivalente em SQL para diferença, utiliza- mos o operador EXISTS, visto anteriormente. Assim, para saber qual cliente não efetuou pedido, é verificado se existe na tabela “Pedido” algum código de pedido que esteja presente nas duas tabelas:
SQL (39):
SELECT clientes.codigo FROM clientes
WHERE NOT EXISTS (SELECT pedido.codcli FROM pedido
WHERE pedido.codcli = clientes.codigo)
A subconsulta fornece a existência de algum registro que atende à condi- ção na sua cláusula WHERE, ou seja, se o código do cliente está presente na tabela “Pedido”. Note que na igualdade o atributo “codcli” da sub- consulta é comparado com o atributo “codigo” da consulta principal. Esta é, então, equivalente à consulta com a cláusula EXCEPT.
123
[
interseção
]
Da mesma forma que a operação de diferença, a representação da ope- ração de interseção é equivalente à AR. Duas tabelas “A” e “B” são ope- radas com interseção fazendo A ∩ B*.
A seguinte interseção em AR pode ser efetuada: πcodigo (clientes) ∩ πcodcli (pedido)
Em SQL, utilizamos a cláusula INTERSECT do padrão SQL para efetuar a interseção. O comando SQL equivalente à consulta supracitada é:
SQL (40): SELECT codigo FROM clientes INTERSECT SELECT codcli FROM pedido Produzindo: codigo 202 221 233 282
Os bancos de dados PostgreSQL, DB2 e Oracle utilizam o INTERSECT. O Microsoft SQL Server, o Sybase, o Microsoft Access, o Interbase e o MySQL não implementam a operação de interse- ção, devendo a mesma ser simulada. O PostgreSQL, o DB2 e o Oracle também suportam o INTERSECT ALL.
* A mesma ressalva feita à operação de união e diferença vale aqui, ou seja, que os esquemas das duas tabelas sejam iguais, contenham os mesmos atributos.
O comando equivalente, para simular o comportamento de interseção, utiliza também a cláusula EXISTS de maneira complementar à diferença:
SQL (41): SELECT clientes.codigo FROM clientes WHERE exists (SELECT pedido.codcli FROM pedido
WHERE pedido.codcli = clientes.codigo)
O comando seria idêntico ao utilizado para a diferença se não fosse a ausência do operador lógico NOT.
[
renomeando tabelas
]
Vimos anteriormente que na AR temos um operador para modificar o nome de uma tabela, o operador de renomeação (ρ). Se quisermos mudar o nome da tabela “Clientes” para “C”, fazemos:
ρC (clientes)
Em SQL, utilizamos a palavra-chave AS para renomear uma tabela. Isso é justificável quando tem o objetivo de simplificar a escrita dos comandos, e, também, quando fazemos referência à mesma tabela mais de uma vez. Desse modo, o seguinte comando, para buscar o nome do cliente para o pedido 1010, pode ser simplificado de:
SELECT cliente.nome FROM pedido, clientes
WHERE pedido.codcli = clientes.codigo AND pedido.codigo = 1010
para:
SQL (42):
SELECT cliente.nome
FROM pedido AS p, clientes AS c WHERE p.codcli = c.codigo AND p.codigo = 1010
125
A consulta equivalente em AR é:
πc.nome (σ p.codigo=1010 e p.codcli=c.codigo(ρp (pedido) × ρc (clientes)))
No próximo exemplo, se quisermos conhecer o código dos clientes que
efetuaram mais de um pedido, podemos fazer:
SQL (43):
SELECT p.codcli FROM pedido AS p WHERE p.valor < ANY (SELECT pedido.valor FROM pedido
WHERE pedido.codcli = p.codcli)
Outro comando SQL, utilizando renomeação de tabelas, pode ser feito para uma consulta na qual queiramos obter o valor maior ou menor dentro de certo atributo. Por exemplo, se quisermos obter o pedido de
maior valor, fazemos:
SQL (44):
SELECT pedido.valor FROM pedido
WHERE pedido.valor NOT IN (SELECT p1.valor
FROM pedido AS p1, pedido AS p2 WHERE pedido.codigo = p1.codigo) AND p1.valor < p2.valor)
Em AR, vimos um exemplo, utilizando diferença, que pode ser afirmado como equivalente ao comando SQL anterior:
πpedido.valor (pedido) − πp1.valor (σp1.valor<p2.valor (ρp1(pedido) × ρp2(pedido)))
[
renomeando atributos
]
Assim como é possível renomear tabelas, também, o é em relação aos atri- butos de uma consulta em SQL. Tal operação tem como objetivo melhorar o formato do resultado de maneira a mostrar outra denominação em vez
do nome dos atributos. Sendo, inclusive, permitido fazer operações mate- máticas com eles. A palavra-chave AS também é utilizada para essa tarefa. Por exemplo, se quisermos modificar o nome dos campos de uma con- sulta para, assim, retornar o código do pedido e o valor para o pedido 1010, fazemos:
SQL (45):
SELECT codcli AS codigo_do_cliente, valor AS total_do_pedido FROM pedido
WHERE código = 1010
O resultado mostrado é:
codigo_do_cliente total_do_pedido
233 1.020,00
Outra variação permitida é “embutir” um atributo constante junto à saída:
SQL (46):
SELECT ‘codigo’ AS titulo, codcli AS codigo_do_cliente, valor AS
total_do_pedido
FROM pedido
WHERE codigo = 1010
Onde “codigo” é uma string constante que aparece à esquerda do código do cliente, e “titulo” é o nome do campo fictício:
titulo codigo_do_cliente total_do_pedido
codigo 233 1.020,00
Algo que aumenta ainda mais o poder de formatação de uma saída em SQL é o uso de operações matemáticas sobre os atributos. Caso quei- ramos mostrar a saída em outro formato de moeda (dólar), podemos multiplicar o atributo pelo valor de conversão (no exemplo, R$2,90 por dólar) na própria cláusula SELECT:
127
SQL (47):
SELECT ’codigo’ AS titulo, codcli AS codigo_do_cliente,
valor*2.90 AS total_do_pedido FROM pedido
WHERE codigo = 1010
E assim:
titulo codigo_do_cliente total_do_pedido
codigo 233 2.958,00
[
agregação
]
Através da agregação, efetuamos uma operação que resulta num valor único para uma lista de valores que aparecem em um atributo. Alguns exemplos são soma de valores e a contagem absoluta de registros. A operação é feita mediante o uso de uma função para o atributo dese- jado, inserido na cláusula SELECT. Podemos renomear a função de agre- gação para apresentação dos dados na tabela de resultado.
Veja as funções de agregação previstas para o padrão SQL no quadro a seguir:
Quadro 4.5 – Funções de agregação para o padrão SQL
Comando de agregação Finalidade
SUM Executa a soma dos valores existentes sobre o atributo especificado. É válido apenas para atributos de tipo numérico.