Esta variação da instrução SELECT permite que uma consulta seja feita dentro do resultado de outra consulta, permitindo a construção de comandos SQL mais flexíveis, suportando o uso de funções agregadoras.
Visão geral
Consulta em resultado de outras consultas bem utilizada quando se deseja, por exemplo, trazer o valor máximo de uma coluna que foi gerada a patir de cálculos com funções de agregação.
Considere o seguinte cenário: temos duas tabelas no banco de dados, uma chamada vendas (Tabela 1), responsável por armazenar as informações de vendas da aplicação, e outra chamada vendas_parcelas (Tabela 2), essa responsável pelo detalhamento do parcelamento das vendas.
ID | ID_CLIENTE | DATA | VALOR |
1 | 1 | 2018-06-07 | 1200.00 |
2 | 5 | 2018-05-17 | 800.00 |
3 | 3 | 2018-07-17 | 620.00 |
4 | 1 | 2018-05-18 | 900.00 |
'
ID | ID_VENDA | NUMERO_PARCELA | VALOR_PARCELA | VENCIMENTO | DATA_PAGEMENTO |
1 | 1 | 1 | 600.00 | 2018-06-10 | NULL |
2 | 1 | 2 | 600.00 | 2018-07-10 | NULL |
3 | 2 | 1 | 200.00 | 2018-05-10 | 2018-05-10 |
4 | 2 | 2 | 200.00 | 2018-06-10 | 2018-06-10 |
5 | 2 | 3 | 200.00 | 2018-07-10 | 2018-07-10 |
6 | 2 | 4 | 200.00 | 2018-08-10 | NULL |
7 | 3 | 1 | 310.00 | 2018-07-10 | 2018-07-10 |
8 | 3 | 2 | 310.00 | 2018-08-10 | 2018-07-10 |
9 | 4 | 1 | 300.00 | 2018-05-20 | 2018-07-17 |
10 | 4 | 2 | 300.00 | 2018-06-20 | 2018-07-17 |
11 | 4 | 3 | 300.00 | 2018-07-20 | 2018-07-17 |
Suponha que desejamos pagar uma comissão aos vendedores, e que para pagar essa comissão, é necessário que a primeira parcela da venda esteja paga. Foi nos solicitado uma consulta no banco de dados para saber quantas comissões serão pagas no mês de julho Para isso, primeiro precisamos pegar todas as vendas que tiveram a primeira parcela paga, e em seguida, verificar o total dessa consulta. Para isso podemos utilizar a query abaixo:
SELECT COUNT(ID) as TOTAL FROM
(
SELECT
V.ID,
V.VALOR,
P.DATA_PAGAMENTO,
P.NUMERO_PARCELA
FROM
VENDAS V, VENDAS_PARCELAS P
WHERE (V.ID = P.ID_VENDA)
AND (P.NUMERO_PARCELA = 1)
AND (P.DATA_PAGAMENTO IS NOT NULL)
AND (month(P.DATA_PAGAMENTO) = 7)
) as tabela
Com a query acima obtivemos o resultado ilustrado na Tabela 3.
TOTAL |
2 |
Esse resultado foi encontrado porque no trecho de código acima, entre as linhas 3 a 7 selecionamos as colunas que virão no resultado da consulta.
Na linha 8 selecionamos as tabelas que serão utilizadas na consulta, VENDAS com o apelido V, e VENDAS_PARCELAS com o apelido P. Em seguida, na linha 9 realizamos uma condicional para garantir que o ID da venda será o mesmo nas duas tabelas.
Nas linhas 10, 11 e 12 é onde definimos que o número da parcela deve ser 1 (primeira parcela) , que a data de pagamento não deve ser nula, ou seja, estar paga, e que o mês do pagamento da parcela deve ser 7 (julho).
Com essa consulta pronta, podemos realizar um novo SELECT nesse resultado, o que é feito nas linhas 1 e 2, onde selecionamos o total de ID encontrado, dando um apelido como TOTAL.
Na linha 13 definimos um apelido para o select que está servindo como base de tabela para a subconsulta.
Sintaxe
SELECT [coluna1, coluna2, … ]
FROM (
SELECT [coluna1, coluna2, … ]
FROM [tabela1, tabela2, … ]
)
Na prática
Exemplo 1
Como outro exemplo podemos aproveitar a consulta que foi feita no exemplo de SELECT AS FIELD, que era responsável não só por trazer todas as vendas, mas também trazer quantidade de parcelas abertas e pagas da tabela. Alterando um pouco o cenário, imagine que agora precisamos listar a partir dessa consulta anterior, somente as vendas que tenham alguma parcela paga. Para isso podemos utilizar a query abaixo e com o resultado da Tabela 4:
SELECT * FROM (
SELECT
V.ID,
V.VALOR,
V.DATA,
(SELECT
COUNT(P.ID_VENDA)
FROM
FN_VENDAS_PARCELAS P
WHERE
(V.ID = P.ID_VENDA) AND
(P.DATA_PAGAMENTO is NULL)
) AS QTD_PARCELAS_ABERTAS,
(SELECT
COUNT(P.ID_VENDA)
FROM
FN_VENDAS_PARCELAS P
WHERE
(V.ID = P.ID_VENDA) AND
(P.DATA_PAGAMENTO is NOT NULL)
) AS QTD_PARCELAS_PAGAS
FROM
VENDAS V
) as TABELA
WHERE
QTD_PARCELAS_PAGAS > 0
ID | VALOR | DATA | QTD_PARCELAS_ABERTAS | QTD_PARCELAS_PAGAS |
2 | 800.00 | 2018-05-17 | 1 | 3 |
3 | 620.00 | 2018-07-17 | 0 | 2 |
4 | 900.00 | 2018-05-18 | 0 | 3 |
Esse resultado foi possível porque fizemos um select dentro de outro select, ou seja, na linha 1 solicitamos todas as informações de uma outra consulta.
Naslinhas 2 a 5 selecionamos as colunas da tabela venda que irão vir na consulta e, logo em seguida, criamos nas linhas 6 a 17 duas colunas novas que estão vindo de uma subconsulta feita na tabela vendas_parcelas. Essas colunas foram nomeadas como QTD_PARCELAS_ABERTAS e QTD_PARCELAS_PAGAS, respectivamente.
Na linha 6 selecionamos o total vendas da tabela vendas_parcelas, através da função agregadora COUNT e, em seguida, nas linhas 9 e 10 definimos que o ID da venda deve ser o mesmo nas duas tabelas, e a data de pagamento deve ser nula, ou seja parcelas abertas. Na linha 11 nomeamos essa consulta como a coluna QTD_PARCELAS_ABERTAS.
Na linha 12 é feito outro select para trazer o total de vendas, através da função agregadora count e, em seguida, nas linhas 15 e 16 definimos que o ID da venda deve ser o mesmo nas duas tabelas, e que a data de pagamento não deve ser nula, ou seja, já estar pago. Na linha 17 nomeamos essa consulta como a coluna QTD_PARCELAS_PAGAS.
Na linha 21 definimos um apelido para a consulta, como TABELA e, logo em seguida, definimos a condição, que será exibir somente os dados que tiverem pelo menos uma parcela paga.
Exemplo 2
Para este exemplo, considere a estrutura de tabelas abaixo, onde temos uma tabela chamada produto (Tabela 5), responsável por armazenar todos os produtos do estoque do cliente, a tabela categoria_produto (Tabela 6), responsável por armazenar todas as categorias existentes no banco de dados, e a tabela venda_produto (Tabela 7), que relaciona os produtos que foram vendidos.
id | nome | preco | Id_categoria |
1 | Bola | 35.00 | 1 |
2 | Patinete | 120.00 | 1 |
3 | Carrinho | 15.00 | 1 |
4 | Skate | 296.00 | 1 |
5 | Notebook | 3500.00 | 2 |
6 | Monitor LG 19 | 450.00 | 2 |
7 | O Diário de Anne Frank | 45.00 | 3 |
8 | O dia do Curinga | 65.00 | 3 |
9 | O mundo de Sofia | 48.00 | 3 |
10 | Através do Espelho | 38.00 | 3 |
id | nome |
1 | Infantil |
2 | Informatica |
3 | Educacional |
id | id_produto | valor | data |
1 | 1 | 35.00 | 2018-05-15 |
2 | 1 | 35.00 | 2018-06-15 |
3 | 1 | 35.00 | 2018-07-15 |
4 | 2 | 120.00 | 2018-07-15 |
5 | 2 | 120.00 | 2018-07-14 |
6 | 3 | 15.00 | 2018-07-15 |
7 | 7 | 45.00 | 2018-07-15 |
8 | 8 | 65.00 | 2018-07-15 |
9 | 8 | 65.00 | 2018-07-16 |
10 | 9 | 48.00 | 2018-07-16 |
11 | 5 | 3500.00 | 2018-07-16 |
12 | 5 | 3500.00 | 2018-07-16 |
13 | 6 | 450.00 | 2018-07-16 |
Podemos fazer uma consulta de todos os produtos da tabela produto, e adicionando uma nova coluna que seria o valor da Média Geral de preços de todos os produtos. Em seguida, realizaremos uma consulta no resultado desta primeiro consulta, trazendo somente aqueles que possuem um preço acima da média de preços. Para isso podemos utilizar a query abaixo:
SELECT
T.*
FROM
(
SELECT
V.*,
(
SELECT
AVG(P.preco)
FROM
produto P
) as MEDIA_GLOBAL
FROM
venda_produto V
) as T
WHERE
T.valor > T.MEDIA_GLOBAL
No trecho de código acima, nas Linhas 1 e 2 selecionamos todos os dados da tabela nomeada T, através do *, e em seguida, na Linha 3 definimos que a consulta será feita com base no resultado de outro subquery, definido nas linhas de 5 a 14.
Observe que na linha 15 informamos uma nome para o resultado da subquery, que será trabalhado como uma tabela. Esse nome é obrigatório para que o SQL entenda de onde deve tirar as informações.
Exemplo 3
Para o exemplo a seguir, considere a estrutura de tabelas abaixo, onde temos a tabela projetos (Tabela 8), a tabela comentarios (Tabela 9), a tabela usuarios (Tabela 10), a tabela likes_por_projeto (Tabela 11) e a tabela likes_por_comentarios (Tabela12).
id | titulo | data |
---|---|---|
1 | Aplicação C# | 2018-04-01 |
2 | Aplicação Ionic | 2018-05-07 |
3 | Aplicação Python | 2018-08-05 |
id | comentario | id_projeto | id_usuario |
---|---|---|---|
1 | A microsoft acertou com essa linguagem! | 1 | 1 |
2 | Parabéns pelo projet! bem legal! | 1 | 3 |
3 | Super interessante! Fácil e rapido! | 2 | 4 |
4 | Cara, que simples fazer um app assim! | 2 | 1 |
5 | Linguagem muito diferente. | 3 | 3 |
6 | Adorei aprender Python! Parabéns! | 3 | 2 |
7 | Muito maneiro esse framework! | 2 | 2 |
id | nome | senha | |
---|---|---|---|
1 | Bruna Luiza | bruninha@gmail.com | abc123. |
2 | Thiago Braga | thiagobraga_1@hotmail.com | pena093 |
3 | Osvaldo Justino | osvaltino@yahoo.com.br | osvaldit1_s |
4 | Gabriel Fernando | gabriel_fnd@gmail.com | gabss34 |
id_projeto | id_usuario |
---|---|
1 | 1 |
1 | 3 |
2 | 1 |
2 | 2 |
2 | 3 |
2 | 4 |
3 | 2 |
id_comentario | id_usuario |
---|---|
7 | 1 |
7 | 2 |
7 | 4 |
Para este exemplo, realizaremos primeiro a query que servirá como fonte de dados para a consulta principal:
SELECT
P.id,
P.titulo,
(SELECT
COUNT(C.id_projeto)
FROM
comentarios C
WHERE
C.id_projeto = P.id ) AS Quantidade_Comentarios
FROM
projetos P
Veja abaixo o resultado da query acima na Tabela 13.
titulo | Quantidade_Comentarios |
---|---|
Aplicação C# | 2 |
Apliocação Ionic | 3 |
Aplicação Python | 2 |
Com base no resultado acima, iremos selecionar apenas o projeto que teve a quantidade de comentarios maior de 2, dessa forma, utilizaremos a query acima como fonte de dados.
SELECT
F.titulo,
F.Quantidade_Comentarios
FROM
(SELECT
P.id,
P.titulo,
(SELECT
COUNT(C.id_projeto)
FROM
comentarios C
WHERE
C.id_projeto = P.id ) AS Quantidade_Comentarios
FROM
projetos P
) as F
WHERE
F.Quantidade_Comentarios > 2
Observe que na query acima, a consulta principal solicita através do FROM as colunas titulo, Quantidade_Comentarios da fonte de dados baseada em uma outra consulta, e por fim, realiza um filtro no resultado através da cláusula WHERE para buscar somente aqueles projetos com a quantidade de comentarios maior que 2.
Sempre após a criação de uma subquery como fonte de dados de uma consulta principal será necessário definir um nome para esta fonte de dados, através da palavra reservada AS.