SQL: JOIN Complexos

Sabemos que no dia a dia de um desenvolvedor a complexidade das consultas SQL podem variar de acordo com o cenário.

Sabemos que no dia a dia de um desenvolvedor a complexidade das consultas SQL podem variar de acordo com o cenário. Pensando por este lado, trouxemos nestes tópicos alguns exemplos do dia a dia que podem lhe ajudar a confeccionar suas queries utilizando o melhor do SQL.

Na prática

Exemplo 1

Considere a tabela tópicos (Tabela 1) onde armazenamos tópicos de um determinado fórum e, em seguida, a tabela likes (Tabela 2) onde armazenamos as curtidas que cada tópico teve.

id_topico titulo data_cadastro
1 Como utilizar JOINs com três tabelas 2016-04-18 12:39:10
2 Por que a minha query não funciona no MySQL? 2016-04-20 12:39:10
3 Erro ao conectar com o Firebird 2016-07-04 12:39:10
4 Usando TOP no POSTGRES 2016-07-23 12:39:10
5 Consulta não gera os resultados como esperado 2016-10-17 12:39:10
6 Listar apenas os dados duplicados no banco de dados 2016-10-18 12:39:10
7 Listar media de preco na tabela 2017-07-21 00:00:00
Tabela 1. Tabela topicos
id_like id_topico data_criacao id_usuario
1 1 2016-07-17 12:41:40 1234
2 1 2016-07-17 12:41:40 432
3 1 2016-08-19 12:41:40 654
4 3 2016-08-22 12:41:40 5688
5 3 2017-08-24 12:41:40 87
6 3 2017-12-18 12:41:40 5443
7 3 2018-01-21 12:41:40 654
8 3 2018-04-22 12:41:40 432
9 2 2018-07-17 12:41:40 5643
10 2 2018-08-16 12:41:40 5456
11 7 2017-12-12 00:00:00 5211
12 4 2017-12-11 00:00:00 5211
13 4 2017-11-10 00:00:00 5456
Tabela 2. Tabela likes

Desejamos listar todos os tópicos que tiveram mais do que duas curtidas durante o mês de julho. Para isso, podemos utilizar a query abaixo:

SELECT t.titulo, count(l.id_like) as total FROM likes l, topico t WHERE l.id_topico = t.id_topico AND month(l.data_criacao) = 7 GROUP BY l.id_topico HAVING count(l.id_like) >= 2

Conforme podemos ver acima, a consulta utiliza a função agregadora count para retornar o total de likes, tendo em vista que esta função é feita na coluna l.id_like, cujo apelido l pertence a tabela likes.

Em seguida, na linha 4 fazemos o SELECT a partir de duas tabelas, definindo um apelido para cada uma delas, l para tabela likes e t para tabela tópico.

Na linha 6 definimos como condição para o filtro o identificador do tópico (id_topico) ser idêntico nas duas tabelas.

Perceba que na linha 7 utilizamos o operador AND para criar uma segunda condição que é o mês ser igual a 7 (julho). Em seguida agrupamos os resultados utilizando o id do topico (id_topico). Esse agrupamento é feito, pois precisamos de apenas um resultado por tópico, ou seja, precisamos agrupar todas as ocorrências em uma só.

Por fim utilizamos um último filtro na consulta com o HAVING, que é o número de likes ser maior ou igual a 2. Observe o resultado na Tabela 3.

titulo total
Como utilizar JOINs com três tabelas 2
Tabela 3. Resultado da busca pelo tópico de mais curtidas no mês 7

Exemplo 2

Ainda utilizando a mesma estrutura do exemplo anterior (Tabela 1 e Tabela 2), partimos para um segundo exemplo. Suponha que precisamos retornar os três dias em que tivemos mais curtidas no mês de julho. Para isso, podemos utilizar a query abaixo e obtemos como resultado a Tabela 4:

SELECT DAY(l.data_criacao) as Dia, count(l.id_like) as total FROM likes l, topico t WHERE l.id_topico = t.id_topico AND month(l.data_criacao) = 7 GROUP BY Dia ORDER BY total DESC LIMIT 0, 3;
dia total
17 3
10 1
Tabela 4. Resultado da consulta dos dias com mais curtidas no mês de julho

Na consulta acima, utilizamos o SELECT para recuperar duas colunas: a primeira com o dia da criação, definindo o apelido DIA, e o total de curtidas, verificando pelo total de ocorrências da coluna l.id_like, através da função de agregação COUNT, onde também definimos um apelido como total entre as linhas 1 e 2.

Em seguida utilizamos o FROM para fazer a seleção a partir de duas tabelas: likes, que usaremos o apelido l e topico com o apelido t entre as linhas 3 e 4.

Em seguida definimos duas condições para filtrar os resultados usando as cláusulas WHERE: a primeira era as tabelas like e topico possuírem o mesmo id_topico e, a data_criaçao ter o mês igual a 7 (julho) entre as linhas 5 e 8.

Em seguida agrupamos os resultados com o GROUP BY por dia e, ordenamos os resultados com o ORDER BY por total em ordem decrescente limitando os resultados a, no máximo, três itens entre as linhas 9 e 12.

Exemplo 3

Podemos aproveitar mais o cenário em outro exemplo. Suponha que seja necessário agora trazer os três tópicos que tiveram mais curtidas ainda no mês de julho. Para isso, podemos utilizar a query abaixo e que resulta na Tabela 5:

SELECT t.titulo, count(l.id_like) as total FROM likes l, topico t WHERE l.id_topico = t.id_topico AND month(l.data_criacao) = 7 GROUP BY l.id_topico ORDER BY total DESC LIMIT 0, 3
titulo total
Como utilizar JOINs com três tabelas 2
Por que a minha query não funciona no MySQL? 1
Usando TOP no POSTGRES 1
Tabela 5. Resultado da consulta

Na consulta acima utilizamos o SELECT para listar os dados das tabelas likes (usando o ALIAS l) e topico (usando o ALIAS t) na linha 4, pedindo as colunas de título da tabela topico e o total usando a função agregadora COUNT de likes na linha 2.

Em seguida utilizamos a cláusula WHERE para definir os filtros de busca, verificando se o id do tópico é igual em ambas as tabelas e, se o mês da criação igual a 7 (julho) entre as linhas 6 e 8.

Para organizar os resultados utilizamos o GROUP BY para agrupar os resultados pelo id_tipo e, em seguida, na linha 10 ordenamos os dados com o ORDER BY.

Para limitar os resultados da query a três linhas utilizamos na linha 12 o LIMIT, começando do registro 0 e parando no 3.

Exemplo 4

Imagine então um outro cenário, onde temos uma tabela vendas (Tabela 6), e uma tabela onde ficam as parcelas, chamada vendas_parcelas, referentes às vendas feitas (Tabela 7).

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
Tabela 6. Tabela vendas
ID ID_VENDA NUMERO_PARCELA VALOR_PARCELA VENCIMENTO DATA_PAGAMENTO
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
Tabela 7. Tabela vendas_parcelas

Com essa estrutura, suponhamos que desejamos fazer uma busca de todas as vendas que tenham todas as parcelas pagas, ou seja, todas as vendas concluídas. Para isso, podemos utilizar a query que tem com resultado a Tabela 8.

SELECT V.ID, V.DATA, V.VALOR FROM VENDAS V WHERE ID NOT IN ( SELECT P.ID_VENDA FROM VENDAS_PARCELAS P WHERE P.ID_VENDA = V.ID AND P.DATA_PAGAMENTO IS NULL ) AND MONTH(V.DATA) = 5
ID DATA VALOR
4 2018-05-18 900.00
Tabela 8. Resultado da consulta de todas as vendas já pagas, sem parcelas em aberto

Na consulta acima, das linhas 1 a 4 selecionamos os campos ID, DATA e VALOR, definindo um apelido para a tabela, como V.

Em seguida, na linha 6, definimos qual a tabela principal de consulta dos dados para VENDAS, e informamos que o seu apelido será V, para facilitar a legibilidade da query.

Na linha 8 definimos a primeira condição da query através do NOT IN, precisamos que o ID da venda não esteja mencionado no resultado de outro select, que é feio na linha posterior.

Na linha 9 fazemos o segundo select da query, buscando na tabela VENDA_PARCELA todas as vendas que tenha a DATA_PAGAMENTO como NULL, ou seja, alguma parcela em aberto e que tenham o ID_VENDA igual ao ID da tabela VENDA, para fazer o vínculo entre as duas tabelas.

Na linha 18 realizamos a segunda condição da query, que é o mês da venda ser igual a maio (5).

Exemplo 5

Pensando em uma abordagem um pouco mais complexa, suponha que nessa mesma estrutura de tabelas, desejamos realizar uma consulta para verificar o pagamento de comissão de nossos vendedores. A comissão só é pega se a primeira parcela da venda for paga, sendo assim só pode vir nessa consulta as vendas que tiverem a primeira parcela dada como paga. Para isso, podemos utilizar a query abaixo:

SELECT V.ID, V.VALOR, P.DATA_PAGAMENTO, P.NUMERO_PARCELA, (V.VALOR * 0.10) as VALOR_COMISSAO FROM VENDAS V, VENDAS_PARCELAS P WHERE (V.ID = P.ID_VENDA) AND (P.NUMERO_PARCELA = 1) AND (P.DATA_PAGAMENTO IS NOT NULL);

No trecho de código acima, entre as linhas 1 a 6 selecionamos as colunas que virão no resultado da consulta. Observe que criamos uma coluna chamada VALOR_COMISSAO que será o valor da conta referente a 10% do valor da venda.

Na linha 7 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 8 realizamos uma condicional para garantir que o ID da venda será o mesmo nas duas tabelas.

Nas linhas 9 e 10 é onde definimos que o número da parcela deve ser 1 (primeira parcela) e que a data de pagamento não deve ser nula, ou seja, estar paga.

Confira também

Artigos relacionados