JOINs com Sequelize, MySQL e Node.Js — parte 2

Edu Marcelino
7 min readMay 28, 2020

--

Na parte 1 deste artigo falamos sobre alguns tipos de consultas do tipo JOIN.

SELECT TWO TABLES

INNER JOIN

LEFT OUTER JOIN

RIGHT OUTER JOIN

Nesta segunda parte iremos nos aprofundar em mais tipos de Join com sequelize.

Recomendo ler a parte 1, se já lei, vamos em frente!

JOINs para a parte 2

Nesta parte do artigo veremos estes tipos de JOIN:

SEMI JOIN

ANTI SEMI JOIN

FULL OUTER JOIN

FULL OUTER JOIN EXCLUSION

TABELAS

Para você não se perder, os exemplos serão sempre com as tabelas criadas de exemplo: movie, category e parental

Tabela 1: movies
Tabela 2: category
Tabela 3: parental

Vamos lá!

SEMI JOIN

Ou também chamado de LEFT SEMI JOIN Subqueries.
O objetivo é o mesmo do INNER JOIN em listar os registros presentes nas duas tabelas relacionadas por suas chaves primaria e estrangeira, ou seja, deve existir nas duas tabelas para ser listado. Porém, no INNER JOIN é possível incluir no resultado as colunas da tabela da direita, o que é possível no SEMI JOIN. Em outras palavras, as únicas colunas que serão exibidas pertencem à tabela da esquerda, enquanto que a tabela da direita serve de filtro da seleção.

http://stevestedman.com/wp-content/uploads/VennDiagram2.pdf

Note que a palavra reservada JOIN não aparece na estrutura, ao invés disso existe apenas um select que retornará dados, e outro select de filtragem.

No sequelize ficará assim:

Observe que a unica coisa que é passada para o primeiro where é se existe um movie.parentalRatingID na tabela parental.id, não importando mais nada.

Na saída teremos:

Veja a saida do INNER JOIN na parte 1 deste artigo e compare. Adianto que são iguais exceto que neste SEMI JOIN não é possível exibir a coluna parental.name.

Vamos ver como ficou o log do sequelize:

Executing (default): SELECT `id`, `name`, `categoryId`, `dubLeg` FROM `movies` AS `movies` WHERE exists (select 1 from parental where id = parentalRatingID ) ORDER BY `movies`.`id` ASC;

ANTI SEMI JOIN

Este tipo é o oposto do SEMI JOIN, ou seja, selecionar os registros da tabela da esquerda onde NÃO exitam registros com o mesmo valor de chave estrangeira.

http://stevestedman.com/wp-content/uploads/VennDiagram2.pdf

No sequelize ficará assim:

No ANTI SEMI JOIN incluímos o not exits na cláusula where, o que fará retornar verdadeiro se o movies.id existir em parental.parentalRatingID.

Na saída teremos:

Veja retornou apenas os registro que estão com a chave estrangeira null.

Vamos ver como ficou o log do sequelize:

Executing (default): SELECT `id`, `name`, `categoryId`, `dubLeg` FROM `movies` AS `movies` WHERE not exists (
select 1 from parental
where id = parentalRatingID ) ORDER BY `movies`.`id` ASC;

FULL OUTER JOIN

Apesar de estar especificado no padrão SQL, este tipo de JOIN não é suportado pelo MySQL e forks como o MariaDB. Mesmo se fosse possível, o Sequelize não possibilitaria este tipo de operação.

Na especificação do SQL o comando tem este formato:

https://www.w3schools.com/sql/sql_join_full.asp

O FULL OUTER JOIN retorna todas as linhas da tabela da direita e todas as linhas da tabela da esquerda, tendo ou não relação de igualdade nas chaves.
É utilizado quando queremos unir duas tabelas obtendo o mesmo resultado de um LEFT OUTER JOIN e RIGHT OUTER JOIN juntos.
Por este motivo podemos simular o mesmo resultado utilizado a palavrá reservada UNION.
UNION é capaz de unificar dois SELECTs conforme a figura abaixo.

Como já citei acima, o Sequelize não suporta operações FULL OUTER JOIN, e mesmo que suportasse teríamos erros no banco de dados pois o sequelize converte os seus comando em comandos SQL antes de enviar ao MySQL.
O Sequelize também não disponibiliza parâmetros para fazermos uma união direta de selects.
Nesta situação teremos que recorrer às RAW QUERIES.

RAW QUERIES são formas de passar diretamente os comandos SQL, sem utilizar os recursos nativos do Sequelize.

Exemplo de Raw query:

sequelize.query( “SELECT table1 WHETE id > 100”)

Faremos então uma chamada UNION entre os SELECTs, simulando assim um FULL OUTER JOIN.

No sequelize ficará assim:

Vamos entender passo a passo este exemplo:

1- Listar o resultado do primeiro select, que é um LEFT JOIN entre as tabelas category e parental. Lembrando que LEFT JOIN retornara todas as linhas da tabela da esquerda.

2- Queremos ver as colunas id e name da tabela category e o campo name da tabela parentel (t1.id, t1.name as Categ, t2.name as Rating)

3- Declaramos UNION, que solicita a união dos resultados.

4- Listar o resultado do segundo select, que é um RIGHT JOIN entre as tabelas category e parental. Lembrando que RIGHT JOIN retornara todas as linhas da tabela da direita.

5- Como um bônus, coloquei um replace para remover os espaços duplos deixados na formatação da string entre a crases ``. Mas isso você pode ignorar.

Na saída teremos:

O que significa esse resultado ?

A tabela category possui 24 registros e está toda ai por conta do LEFT JOIN.
Os ids de 1 a 6 também existem na tabela parental, e por isso a coluna Rating está populada (lembrando que renomeamos a coluna t2.name para Rating no select)

À partir do id 7, nenhum registro existe na coluna parental, e por isso recebe NULL. Já as últimas duas linhas possuem ids que não existem na tabela category, mas existe na tabela parental, e por isso estão ali informados e null nas demais colunas.

Releia, de uma respirada, porque é mesmo um pouco confuso. Mas entendendo bem o FULL OUTER JOIN, os outros todos ficam muito fáceis.

O sequelize nos retornará em log o mesmo select que enviamos, já que usamos RAW QUERY:

Executing (default): SELECT t1.id, t1.name as Categ, t2.name as Rating FROM category t1 LEFT OUTER JOIN parental t2 ON t1.id = t2.id UNION SELECT t1.id, t1.name as Categ, t2.name as Rating FROM category as t1 RIGHT OUTER JOIN parental as t2 ON t1.id = t2.id

FULL OUTER JOIN EXCLUSION

Este comando frito minha cabeça e ainda tem mais?

Sim, mais um pouco, aguente!

Você já sabe que o FULL OUTER JOIN traz tudo, e se não existir ele seta com null.

Mas agora, queremos todas as linhas de ambas as colunas excluindo as que não coincidem. Ou seja, se tem na tabela 1 mas não tem na tabela 2, não mostre, e vice-versa.

No sequelize ficará assim:

Da mesma forma que o anterior usamos RAW QUERY, e apenas incluímos o WHERE com is not null, para não trazer valore nulos.

Na saída teremos:

Então ai está, todos os ids (pois a validação é por ids t1.id = t2.id), que são comuns a abas tabelas estão presentes.

Vamos ver como ficou o log do sequelize:

Executing (default): SELECT t1.id, t1.name as Categ, t2.name as Rating FROM category t1 LEFT OUTER JOIN parental t2 ON t1.id = t2.id WHERE t2.id is not null UNION SELECT t1.id, t1.name as Categ, t2.name as Rating FROM category as t1 RIGHT OUTER JOIN parental as
t2 ON t1.id = t2.id WHERE t1.id is not null

Ufa! esse foi mais complicado…

Espero que de alguma forma passa ter ajudado você a compreender melhor o mundo dos JOINs.

Todos os arquivos deste artigo estão disponíveis no github

https://github.com/emarcelino3/sequelise-joins

Deixe seu comentário e até a próxima!

--

--