MariaDB
 sql >> Base de Dados >  >> RDS >> MariaDB

Operadores de conjunto SQL MariaDB


Os operadores de conjunto são os operadores SQL que lidam com a combinação, de diferentes maneiras, de diferentes conjuntos de resultados. Digamos que você tenha dois SELECT diferentes s que você deseja combinar em um único conjunto de resultados, os operadores de conjunto entram em ação. MariaDB tem apoiado o UNION e UNION ALL set por um longo tempo, e estes são de longe os operadores de conjunto SQL mais comuns.

Mas estamos nos adiantando aqui, deixe-me primeiro explicar os operadores de conjunto SQL que temos e como eles funcionam. Se você quiser experimentar, você pode usar sua implantação existente do MariaDB Server ou experimentar isso em um banco de dados em nuvem MariaDB SkySQL.

UNION e UNIÃO TODOS


A UNION e UNION ALL operadores de conjunto adicionam o resultado de dois ou mais conjuntos de resultados. Vamos começar com UNION ALL e UNION será então uma variação de UNION ALL .



Vamos dar uma olhada no que parece no SQL. Vamos supor que administramos uma loja virtual e que, para os produtos que vendemos, temos um estoque. Agora queremos ver todos os produtos que estão em pedido ou em estoque, uma consulta para isso seria algo assim:
 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id UNION ALL SELECT i.prod_id, p.prod_name DO inventário i JOIN produtos p ON i.prod_id =p.id; 

Esta é, na teoria dos conjuntos, a UNION dos conjuntos de produtos que foram pedidos e dos conjuntos de produtos que estão em estoque. O que é bom em teoria, mas há um problema com o resultado dessa consulta. O problema é que um produto que aparece nos pedidos e no estoque, ou em vários lugares do estoque, aparecerá mais de uma vez na saída. Esse problema é o motivo pelo qual UNION ALL não é muito usado e, em vez disso, UNION DISTINCT (DISTINCT é o padrão e pode ser ignorado) é usado. Por exemplo:
SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id UNION SELECT i.prod_id, p.prod_name FROM inventário i JOIN produtos p ON i.prod_id =p.id; 
Com esta consulta, um produto encomendado ou existente no inventário é listado apenas uma vez. Observe que quando estamos removendo duplicatas aqui, são os valores que são comparados, portanto, duas linhas com os mesmos valores na mesma coluna são consideradas iguais, mesmo que os valores venham de tabelas ou colunas diferentes.

Para ser honesto, porém, não há nada na consulta acima que não possa ser feito com um SELECT normal dos produtos tabela e algumas junções. De certa forma, um UNION pode ser mais fácil de ler. Por outro lado, se quisermos ter uma lista de produtos em pedido ou em estoque, e também quisermos saber qual era, uma consulta seria algo assim:
 SELECT 'No pedido', oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id UNION SELECT 'Inventory', i.prod_id, p.prod_name DO inventário i JOIN produtos p ON i.prod_id =p.id;

Aqui está uma consulta que não é fácil de fazer com um simples SELECT dos produtos pois estamos olhando para a mesma linha da tabela de produtos duas vezes (uma vez para os order_items e uma vez para o inventário ).

Mais operadores de conjunto SQL


Com o MariaDB Server 10.3 vieram dois novos operadores de conjunto SQL, amplamente introduzidos para aprimorar a compatibilidade com o Oracle, mas esses operadores são úteis por si só. O MariaDB Server 10.4 adiciona a capacidade de controlar a precedência do operador de conjunto. Vamos dar uma olhada nisso também. Sem a capacidade de controlar a precedência do operador, os operadores de conjunto nem sempre funcionam como você deseja ou espera.

Os novos operadores de conjunto SQL são INTERSECT e EXCEPT e são úteis, principalmente ao usar análises. Além disso, embora JOIN s e outras construções podem ser usadas com frequência, os operadores de conjunto SQL permitem uma sintaxe SQL que pode ser mais fácil de ler e entender. E, se você tem aplicativos Oracle que está migrando para o MariaDB, a utilidade desses operadores é óbvia.


O operador de conjunto INTERSECT


O INTERSECT O operador retornará todos os itens que existem em dois ou mais conjuntos ou, em termos SQL, todas as linhas que existem em dois conjuntos de resultados. Nesse caso, é criada uma seção transversal dos dois conjuntos de itens. Em termos de SQL, significa que apenas as linhas que existem em ambos os conjuntos são retornadas, portanto, se eu quiser verificar quais produtos tenho em pedido e quais também estão em estoque, uma consulta pode ficar assim:
 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id INTERSECT SELECT i.prod_id, p.prod_name FROM inventário i JOIN produtos p ON i.prod_id =p.id; 
Novamente, essa consulta pode ser construída usando um JOIN nos produtos tabela, mas a consulta acima é um pouco mais clara sobre o que estamos tentando alcançar.

O operador EXCEPT set




No caso do EXCEPT operador, queremos os itens que estão em um dos conjuntos, mas não no outro. Então, novamente usando o exemplo acima, se quisermos ver os produtos que temos no pedido, mas para os quais não temos estoque, poderíamos escrever uma consulta como esta:
 SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id EXCEPT SELECT i.prod_id, p.prod_name FROM inventário i JOIN produtos p ON i.prod_id =p.id; 
Novamente, existem outras maneiras de escrever essa consulta específica, mas para outras consultas mais avançadas quando combinamos dados de duas tabelas diferentes, esse não é o caso.

Combinando vários operadores de conjunto


Você pode combinar mais de 2 operadores de conjunto se isso for útil. Por exemplo, vejamos se encontramos produtos que estão encomendados e foram entregues ou estão em estoque. O SQL para isso seria algo assim:
SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FROM entregas d JOIN produtos p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name DO inventário i JOIN products p ON i.prod_id =p.id;

Para expressar isso em linguagem simples, o que está acontecendo é que primeiro verifico quais produtos estão em ordem e quais foram entregues, e depois combino esse conjunto de produtos com todos os produtos do estoque. Qualquer produto que não esteja no conjunto de resultados não está no inventário mas pode estar em ordem ou pode ter sido entregue, mas não ambos.

Mas agora, vamos expressar isso de forma diferente e ver o que acontece. Quero uma lista de todos os produtos que estão em estoque ou que foram entregues e estão sob encomenda. O SQL seria algo assim, semelhante ao SQL acima, mas um pouco diferente:
 SELECT i.prod_id, p.prod_name DO inventário i JOIN produtos p ON i.prod_id =p.id UNION SELECT oi.prod_id, p.prod_name FROM order_items oi JOIN produtos p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name FROM entregas d JOIN produtos p ON d.prod_id =p.id;

Como você interpreta isso então? Você lista os produtos que estão em estoque e que estão sob encomenda e os produtos que estão sendo entregues? Isto é o que parece, certo? É só que INTERSECT (e EXCEPT para esse assunto) tem precedência sobre UNION . As duas instruções SQL produzem o mesmo conjunto de resultados, pelo menos no MariaDB e é assim que o SQL Standard diz que as coisas devem funcionar. Mas há uma exceção, Oracle.

Como isso funciona no Oracle


A Oracle teve todos os quatro operadores de conjunto SQL (UNION , UNION ALL , INTERSECT e EXCEPT ) por muito tempo, muito antes de serem padronizados, então sua implementação é um pouco diferente. Vamos tentar com as tabelas acima e inserir alguns dados nelas. Os dados são muito simples e refletem uma empresa não tão bem sucedida, mas funciona como um exemplo, e estamos mostrando apenas as colunas relevantes aqui.


Tabela produtos order_items inventário entregas
Coluna id_prod prod_name order_id id_prod id_prod id_prod
Dados 1 Vaso Azul 1 1 1 2
2 Vaso Vermelho 2 1 2 3
3 Tapete Vermelho 2 3

Com os dados no lugar, vamos olhar para a última instrução SQL acima novamente. Há um recurso que permite controlar a precedência, e é usar parênteses ou colchetes (Introduzido no MariaDB 10.4, consulte https://jira.mariadb.org/browse/MDEV-11953) e usá-los para ilustrar o que está acontecendo, a declaração ficaria assim:
 MariaDB> SELECT i.prod_id, p.prod_name -> FROM Inventory i JOIN products p ON i.prod_id =p.id -> UNION -> (SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN products p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM entregas d JOIN produtos p ON d.prod_id =p.id); +---------+------------+ | prod_id | nome_prod | +---------+------------+ | 1 | Vaso Azul | | 2 | Vaso Vermelho | | 3 | Tapete Vermelho | +---------+------------+ 3 linhas no conjunto (0,001 seg)

Agora, vamos usar a mesma técnica para fazer com que os três componentes da consulta funcionem em ordem estrita:
 MariaDB> (SELECT i.prod_id, p.prod_name -> FROM Inventory i JOIN products p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN products p ON oi.prod_id =p.id) -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM entregas d JOIN produtos p ON d.prod_id =p.id; +---------+------------+ | prod_id | nome_prod | +---------+------------+ | 2 | Vaso Vermelho | | 3 | Tapete Vermelho | +---------+------------+ 2 linhas no conjunto (0,001 seg)

E por último sem parênteses:
 MariaDB [teste]> SELECT i.prod_id, p.prod_name -> DO inventário i JOIN produtos p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produtos p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM entregas d JOIN produtos p ON d.prod_id =p.id; +---------+------------+ | prod_id | nome_prod | +---------+------------+ | 1 | Vaso Azul | | 2 | Vaso Vermelho | | 3 | Tapete Vermelho | +---------+------------+ 3 linhas no conjunto (0,001 seg)

Vemos que o MariaDB, seguindo o padrão, assumiu que INTERSECT tem precedência sobre UNION . O que nos leva à Oracle. Vamos tentar o SQL acima no Oracle usando sqlplus:
 SQL> SELECT i.prod_id, p.prod_name 2 FROM inventário i JOIN produtos p ON i.prod_id =p.id 3 UNION 4 SELECT oi.prod_id, p.prod_name 5 FROM order_items oi JOIN produtos p ON oi.prod_id =p.id 6 INTERSECT 7 SELECT d.prod_id, p.prod_name 8 FROM entregas d JOIN produtos p ON d.prod_id =p.id; PROD_ID PROD_NAME ---------- ------------------------------ 2 Vasos Vermelhos 3 Tapetes Vermelhos 
O que está acontecendo aqui, você pergunta? Bem, a Oracle não segue o padrão. Os diferentes operadores de conjunto são tratados como iguais e nenhum tem precedência sobre o outro. Esse é um problema quando você está migrando aplicativos do Oracle para o MariaDB, e o pior é que essa diferença é meio difícil de encontrar. Nenhum erro é produzido e os dados são retornados e, em muitos casos, os dados corretos são retornados. Mas, em alguns casos, onde os dados são como em nosso exemplo acima, os dados errados são retornados, o que é um problema.

Efeito na migração de dados


Então, como lidamos com isso se estamos migrando um aplicativo do Oracle para o MariaDB? Existem algumas opções:
  • Reescreva o aplicativo para que ele assuma que os dados retornados de uma consulta como essa estão de acordo com o SQL Standard e o MariaDB.
  • Reescreva as instruções SQL, usando colchetes, para que o MariaDB retorne os mesmos dados que o Oracle
  • Ou, e esta é a maneira mais inteligente, use o MariaDB SQL_MODE=Oracle configuração.

Para a última e mais inteligente maneira de funcionar, precisamos executar o MariaDB 10.3.7 ou superior (isso foi sugerido por você em https://jira.mariadb.org/browse/MDEV-13695). Vamos verificar como isso funciona. Compare o resultado deste SELECT com o Oracle acima (que produz o mesmo resultado) e o do MariaDB acima (que não):
 MariaDB> set SQL_MODE=Oracle; Consulta OK, 0 linhas afetadas (0,001 s) MariaDB> SELECT i.prod_id, p.prod_name -> FROM inventário i JOIN produtos p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi JOIN produtos p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM entregas d JOIN produtos p ON d.prod_id =p.id; +---------+------------+ | prod_id | nome_prod | +---------+------------+ | 2 | Vaso Vermelho | | 3 | Tapete Vermelho | +---------+------------+ 2 linhas no conjunto (0,002 seg)

Como você pode ver, quando SQL_MODE está definido como Oracle , MariaDB realmente se comporta como Oracle. Esta não é a única coisa que SQL_MODE=Oracle obviamente, mas é uma das áreas menos conhecidas.

Conclusão


Os operadores de conjunto INTERSECT e EXCEPT não são muito usados, embora apareçam aqui e ali, e haja alguns usos para eles. Os exemplos neste blog servem mais para ilustrar como esses operadores funcionam do que para mostrar usos realmente bons para eles. Provavelmente existem exemplos melhores. No entanto, quando você está migrando do Oracle para o MariaDB, os operadores de conjunto SQL são realmente úteis, pois muitos aplicativos Oracle os usam e, como pode ser visto, o MariaDB pode ser levado a funcionar como o Oracle, que não é padrão, mas ainda serve a um propósito. Mas, por padrão, é claro que o MariaDB funciona como deveria e segue o SQL Standard.

Feliz SQL'ing
/Karlsson