Consultas SQL de União Internas
Detalhamos estas consultas da linguagem SQL tão importantes para o bom desenvolvimento de um banco de dados.Por Claudio
Publicado em: 07/12/07
Consultas de Combinação entre tabelas
As vinculações entre tabelas se realizam mediante a cláusula INNER que combina registros de duas tabelas sempre que tiver concordância de valores em um campo comum. Sua sintaxe é:
SELECT campos FROM tb1 INNER JOIN tb2 ON
tb1.campo1 comp tb2.campo2
Onde:
| tb1, tb2 | São os nomes das tabelas desde as que se combinam os registros. |
| campo1, campo2 | São os nomes dos campos que se combinam. Se não são numéricos, os campos devem ser do mesmo tipo de dados e conter o mesmo tipo de dados, porém não tem que ter o mesmo nome. |
| comp | É qualquer operador de comparação relacional: =, <,<>, <=, =>, ó >. |
Pode-se utilizar uma operação INNER JOIN em qualquer cláusula FROM. Isto cria uma combinação por equivalência, conhecida também como união interna. As combinações equivalentes são as mais comuns; estas combinam os registros de duas tabelas sempre que tiver concordância de valores em um campo comum a ambas tabelas. Pode-se utilizar INNER JOIN com as tabelas Departamentos e Empregados para selecionar todos os empregados de cada departamento. Do contrário, para selecionar todos os departamentos (inclusive se algum deles não tiver nenhum empregado atribuído) se emprega LEFT JOIN ou todos os empregados (inclusive se algum não estiver atribuído a nenhum departamento), neste caso RIGHT JOIN.
Se se tenta combinar campos que contenham dados Memo ou Objeto OLE, se produz um erro. Podem-se combinar dois campos numéricos quaisquer, inclusive se são de diferentes tipos de dados. Por exemplo, pode combinar um campo Numérico para o que a propriedade Size de seu objeto Field está estabelecida como Inteiro, e um campo Contador.
O exemplo seguinte mostra como poderia combinar as tabelas Categorias e Produtos baseando-se no campo IDCategoria:
SELECT
NomeCategoria, NomeProduto
FROM
Categorias
INNER JOIN
Produtos
ON
Categorias.IDCategoria = Produtos.IDCategoria
No exemplo anterior, IDCategoria é o campo combinado, porém não está incluído na saída da consulta já que não está incluído na instrução SELECT. Para incluir o campo combinado, incluir o nome do campo na instrução SELECT, neste caso, Categorias.IDCategoria.
Também se pode linkar várias cláusulas ON em uma instrução JOIN, utilizando a seguinte sintaxe:
SELECT campos FROM tabela1 INNER JOIN tabela2
ON (tb1.campo1 comp tb2.campo1 AND ON tb1.campo2 comp tb2.campo2)
OR ON (tb1.campo3 comp tb2.campo3)
Também pode aninhar instruções JOIN utilizando a seguinte sintaxe:
SELECT campos FROM tb1 INNER JOIN (tb2 INNER JOIN [( ]tb3
[INNER JOIN [( ]tablax [INNER JOIN ...)]
ON tb3.campo3 comp tbx.campox)]
ON tb2.campo2 comp tb3.campo3)
ON tb1.campo1 comp tb2.campo2
Um LEFT JOIN ou um RIGHT JOIN pode se aninhar dentro de um INNER JOIN, porém um INNER JOIN não pode se aninhar dentro de um LEFT JOIN ou um RIGHT JOIN.
Exemplo:
SELECT DISTINCT
Sum(PrecoUnitario * Quantidade) AS Sales,
(Nome + ' ' + Sobrenome) AS Name
FROM
Empregados
INNER JOIN(
Pedidos
INNER JOIN
DetalhesPedidos
ON
Pedidos.IdPedido = DetalhesPedidos.IdPedido)
ON
Empregados.IdEmpregado = Pedidos.IdEmpregado
GROUP BY
Nome + ' ' + Sobrenome
(Cria duas combinações equivalentes: uma entre as tabelas Detalhes de pedidos e Pedidos, e a outra entre as tabelas Pedidos e Empregados. Isto é necessário já que a tabela Empregados não contem dados de vendas e a tabela Detalhes de pedidos não contem dados dos empregados. A consulta produz uma lista de empregados e suas vendas totais.)
Se empregarmos a cláusula INNER na consulta se selecionarão só aqueles registros da tabela da que tivermos escrito à esquerda de INNER JOIN que contenham ao menos um registro da tabela que tivermos escrito à direita. Para solucionar isto temos duas cláusulas que substituem a palavra-chave INNER, estas cláusulas são LEFT e RIGHT. LEFT toma todos os registros da tabela da esquerda embora não tenha nenhum registro na tabela da esquerda. RIGHT realiza a mesma operação, porém ao contrário, toma todos os registros da tabela da direita embora não tenha nenhum registro na tabela da esquerda.
A sintaxe exposta anteriormente pertence a ACCESS, onde todas as sentenças com a sintaxe funcionam corretamente. Os manuais de SQL-SERVER dizem que esta sintaxe é incorreta e que há que adicionar a palavra reservada OUTER: LEFT OUTER JOIN e RIGHT OUTER JOIN. Na prática funciona corretamente de uma forma ou de outra.
Não obstante, os INNER JOIN ORACLE não é capaz de interpretá-los, mas existe uma sintaxe em formato ANSI para os INNER JOIN que funcionam em todos os sistemas. Tomando como referência a seguinte sentença:
SELECT
Faturas.*,
Alvaras.*
FROM
Faturas
INNER JOIN
Alvaras
ON
Faturas.IdAlvara = Alvaras.IdAlvara
WHERE
Faturas.IdCliente = 325
A transformação desta sentença a formato ANSI seria a seguinte:
SELECT
Faturas.*,
Alvaras.*
FROM
Faturas, Alvaras
WHERE
Faturas.IdAlvara = Alvaras.IdAlvara
AND
Faturas.IdCliente = 325
Como se pode observar as mudanças realizadas foram as seguintes:
- Todas as tabelas que intervêm na consulta se especificam na cláusula FROM.
- As condições que vinculam às tabelas se especificam na cláusula WHERE e se vinculam mediante o operador lógico AND.
Referente aos OUTER JOIN, não funcionam em ORACLE e ademais conheço uma sintaxe que funciona nos três sistemas. A sintaxe em ORACLE é igual à sentença anterior, porém adicionando os caracteres (+) atrás do nome da tabela na qual desejamos aceitar valores nulos, isto equivale a um LEFT JOIN:
SELECT
Faturas.*,
Alvaras.*
FROM
Faturas, Alvaras
WHERE
Faturas.IdAlvara = Alvaras.IdAlvara (+)
AND
Faturas.IdCliente = 325
E isto a um RIGHT JOIN:
SELECT
Faturas.*,
Alvaras.*
FROM
Faturas, Alvaras
WHERE
Faturas.IdAlvara (+) = Alvaras.IdAlvara
AND
Faturas.IdCliente = 325
Em SQL-SERVER se pode utilizar uma sintaxe parecida, neste caso não se utiliza os caracteres (+), e sim os caracteres =* para o LEFT JOIN e *= para o RIGHT JOIN.
Consultas de Auto-combinação
A auto-combinação se utiliza para unir uma tabela consigo mesma, comparando valores de duas colunas com o mesmo tipo de dados. A sintaxe na seguinte:
SELECT
alias1.coluna, alias2.coluna, ...
FROM
tabla1 as alias1, tabela2 as alias2
WHERE
alias1.coluna = alias2.coluna
AND
outras condicoes
Por exemplo, para visualizar o número, nome e posto de cada empregado, junto com o número, nome e posto do supervisor de cada um deles se utilizaria a seguinte sentença:
SELECT
t.num_emp, t.nome, t.posto, t.num_sup,s.nome, s.posto
FROM
empregados AS t, empregados AS s
WHERE
t.num_sup = s.num_emp
Consultas de Combinações não Comuns
A maioria das combinações está baseada na igualdade de valores das colunas que são o critério da combinação. As não comuns se baseiam em outros operadores de combinação, tais como NOT, BETWEEN, <>, etc.
Por exemplo, para listar o grau salarial, nome, salário e posto de cada empregado ordenando o resultado por grau e salário haveria que executar a seguinte sentença:
SELECT
graus.grau,empregados.nome, empregados.salario, empregados.posto
FROM
empregados, graus
WHERE
empregados.salario BETWEEN grados.salarioinferior And grados.salariosuperior
ORDER BY
grados.grado, empregados.salario
Para listar o salário médio dentro de cada grau salarial haveria que lançar esta outra sentença:
SELECT
graus.grau, AVG(empregados.salario)
FROM
empregados, graus
WHERE
empregados.salario BETWEEN graus.salarioinferior And graus.salariosuperior
GROUP BY
graus.grau
CROSS JOIN (SQL-SERVER)
Utiliza-se em SQL-SERVER para realizar consultas de união. Suponhamos que temos uma tabela com todos os autores e outra com todos os livros. Se desejássemos obter uma listagem combinando ambas tabelas de tal forma que cada autor aparecesse junto a cada título, utilizaríamos a seguinte sintaxe:
SELECT
Autores.Nome, Livros.Titulo
FROM
Autores CROSS JOIN Livros
SELF JOIN
SELF JOIN é uma técnica empregada para conseguir o produto cartesiano de uma tabela consigo mesma. Sua utilização não é muito freqüente, mas colocaremos algum exemplo de sua utilização.
Suponhamos a seguinte tabela (O campo autor é numérico, embora para ilustrar o exemplo utilize o nome):
| Autores |
| Código (Código do livro) | Autor (Nome do Autor) |
| B0012 | 1. Francisco López |
| B0012 | 2. Javier Alonso |
| B0012 | 3. Marta Rebolledo |
| C0014 | 1. Francisco López |
| C0014 | 2. Javier Alonso |
| D0120 | 2. Javier Alonso |
| D0120 | 3. Marta Rebolledo |
Queremos obter, para cada livro, pares de autores:
SELECT
A.Codigo, A.Autor, B.Autor
FROM
Autores A, Autores B
WHERE
A.Codigo = B.Codigo
O resultado é o seguinte:
| Código | Autor | Autor |
| B0012 | 1. Francisco López | 1. Francisco López |
| B0012 | 1. Francisco López | 2. Javier Alonso |
| B0012 | 1. Francisco López | 3. Marta Rebolledo |
| B0012 | 2. Javier Alonso | 2. Javier Alonso |
| B0012 | 2. Javier Alonso | 1. Francisco López |
| B0012 | 2. Javier Alonso | 3. Marta Rebolledo |
| B0012 | 3. Marta Rebolledo | 3. Marta Rebolledo |
| B0012 | 3. Marta Rebolledo | 2. Javier Alonso |
| B0012 | 3. Marta Rebolledo | 1. Francisco López |
| C0014 | 1. Francisco López | 1. Francisco López |
| C0014 | 1. Francisco López | 2. Javier Alonso |
| C0014 | 2. Javier Alonso | 2. Javier Alonso |
| C0014 | 2. Javier Alonso | 1. Francisco López |
| D0120 | 2. Javier Alonso | 2. Javier Alonso |
| D0120 | 2. Javier Alonso | 3. Marta Rebolledo |
| D0120 | 3. Marta Rebolledo | 3. Marta Rebolledo |
| D0120 | 3. Marta Rebolledo | 2. Javier Alonso |
Como podemos observar, os pares de autores se repetem em cada um dos livros, podemos omitir estas repetições da seguinte forma:
SELECT
A.Codigo, A.Autor, B.Autor
FROM
Autores A, Autores B
WHERE
A.Codigo = B.Codigo AND A.Autor < B.Autor
O resultado agora é o seguinte:
| Código | Autor | Autor |
| B0012 | 1. Francisco López | 2. Javier Alonso |
| B0012 | 1. Francisco López | 3. Marta Rebolledo |
| C0014 | 1. Francisco López | 2. Javier Alonso |
| D0120 | 2. Javier Alonso | 3. Marta Rebolledo |
Agora temos um conjunto de resultados em formato Autor - CoAutor.
Se na tabela de empregados quiséssemos extrair todos os possíveis pares que podemos realizar, utilizaríamos a seguinte sentença:
SELECT
Homens.Nome, Mulheres.Nome
FROM
Empregados Homem, Empregados Mulheres
WHERE
Homem.Sexo = 'Homem' AND
Mulheres.Sexo = 'Mulher' AND
Homens.Id <>Mulheres.Id
Para concluir suponhamos a seguinte tabela:
| Empregados |
| Id | Nome | SeuChefe |
| 1 | Marcos | 6 |
| 2 | Lucas | 1 |
| 3 | Ana | 2 |
| 4 | Eva | 1 |
| 5 | Juan | 6 |
| 6 | Antonio | |
Queremos obter um conjunto de resultados com o nome do empregado e o nome de seu chefe:
SELECT
Empre.Nome, Chefes.Nome
FROM
Empregados Empre, Empregados Chefe
WHERE
Empre.SeuChefe = Chefes.Id
Comentários do artigo