sql >> Base de Datos >  >> RDS >> MariaDB

Operadores de conjuntos SQL de MariaDB

Los operadores de conjuntos son los operadores de SQL que se ocupan de combinar, de diferentes maneras, diferentes conjuntos de resultados. Digamos que tienes dos SELECT diferentes s que desea combinar en un solo conjunto de resultados, los operadores de conjunto entran en juego. MariaDB ha estado apoyando a UNION y UNION ALL operadores de conjuntos durante mucho tiempo, y estos son, con mucho, los operadores de conjuntos de SQL más comunes.

Pero nos estamos adelantando aquí, primero permítanme explicar los operadores de conjuntos de SQL que tenemos y cómo funcionan. Si quiere probar esto, puede usar su implementación existente de MariaDB Server, o probar esto en una base de datos en la nube de MariaDB SkySQL.

UNIÓN y UNIÓN TODOS

La UNION y UNION ALL Los operadores de conjunto agregan el resultado de dos o más conjuntos de resultados. Comencemos con UNION ALL y UNION será entonces una variación de UNION ALL .

Echemos un vistazo a cómo se ve en SQL. Supongamos que tenemos una tienda web y que tenemos un inventario de los productos que vendemos. Ahora queremos ver todos los productos que están en pedido o en inventario, una consulta para esto se vería así:

 SELECT oi.prod_id, p.prod_name
   FROM order_items oi JOIN products p ON oi.prod_id = p.id
 UNION ALL
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Esto es, en teoría de conjuntos, la UNION de los conjuntos de productos que se han pedido y los conjuntos de productos que están en inventario. Lo cual está bien en teoría, pero hay un problema con el resultado de esta consulta. El problema es que un producto que aparece tanto en los pedidos como en el inventario, o en varios lugares del inventario, aparecerá más de una vez en la salida. Este problema es el motivo por el que UNION ALL no se usa mucho y en su lugar UNION DISTINCT (DISTINCT es el valor predeterminado y se puede ignorar) se utiliza. Por ejemplo:

SELECT oi.prod_id, p.prod_name
   FROM order_items oi JOIN products p ON oi.prod_id = p.id
 UNION
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Con esta consulta, un producto que está en pedido o existe en el inventario solo se enumera una vez. Tenga en cuenta que cuando eliminamos duplicados aquí, son los valores los que se comparan, por lo que dos filas con los mismos valores en la misma columna se consideran iguales, aunque los valores provengan de diferentes tablas o columnas.

Sin embargo, para ser honesto, no hay nada en la consulta anterior que no se pueda hacer con un SELECT normal. de los productos mesa y algunas uniones. En cierto modo, una UNION puede ser más fácil de leer. Por otro lado, si queremos tener una lista de productos en pedido o en el inventario, y también queremos saber cuál es, entonces una consulta sería algo como esto:

 SELECT 'On order', oi.prod_id, p.prod_name
   FROM order_items oi JOIN products p ON oi.prod_id = p.id
 UNION
 SELECT 'Inventory', i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Aquí hay una consulta que no es fácil de hacer con un simple SELECT de los productos ya que estamos viendo la misma fila de la tabla de productos dos veces (una vez para order_items y una vez para el inventario ).

Más operadores de conjuntos de SQL

Con MariaDB Server 10.3 llegaron dos nuevos operadores de conjuntos de SQL, introducidos en gran medida para mejorar la compatibilidad con Oracle, pero estos operadores son útiles por derecho propio. MariaDB Server 10.4 luego agrega la capacidad de controlar la precedencia de operadores establecidos. Echaremos un vistazo a eso también. Sin la capacidad de controlar la precedencia de los operadores, los operadores establecidos no siempre funcionan como usted desearía o esperaría.

Los nuevos operadores de conjuntos de SQL son INTERSECT y EXCEPT y son útiles, particularmente cuando se usan análisis. Además, aunque JOIN s y otras construcciones a menudo se pueden usar en su lugar, los operadores de conjuntos de SQL permiten una sintaxis de SQL que puede ser más fácil de leer y comprender. Y, si tiene aplicaciones de Oracle que está migrando a MariaDB, la utilidad de estos operadores es obvia.

El operador de conjuntos INTERSECT

El INTERSECT El operador devolverá todos los elementos que existen en dos o más conjuntos o, en términos de SQL, todas las filas que existen en dos conjuntos de resultados. En este caso, se crea una sección transversal de los dos conjuntos de elementos. En términos de SQL, significa que solo se devuelven las filas que existen en ambos conjuntos, por lo que si quiero verificar qué productos tengo ordenados y cuáles también están en el inventario, una consulta podría verse así:

 SELECT oi.prod_id, p.prod_name
   FROM order_items oi JOIN products p ON oi.prod_id = p.id
 INTERSECT
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Nuevamente, esta consulta podría construirse usando un JOIN sobre los productos table, pero la consulta anterior es un poco más clara sobre lo que estamos tratando de lograr.

El operador de conjunto EXCEPTO

En el caso de EXCEPT operador, queremos los elementos que están en uno de los conjuntos, pero no en el otro. Entonces, usando nuevamente el ejemplo anterior, si queremos ver los productos que tenemos pedidos pero para los cuales no tenemos inventario, podríamos escribir una consulta como esta:

 SELECT oi.prod_id, p.prod_name
   FROM order_items oi JOIN products p ON oi.prod_id = p.id
 EXCEPT
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Nuevamente, hay otras formas de escribir esta consulta en particular, pero para otras consultas más avanzadas cuando combinamos datos de dos tablas diferentes, este no es el caso.

Combinar varios operadores de conjuntos

Puede combinar más de 2 operadores de conjuntos si esto es útil. Por ejemplo, veamos si podemos encontrar productos que están en pedido y han sido entregados o están en stock. El SQL para esto se vería así:

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 deliveries d JOIN products p ON d.prod_id = p.id
 UNION
 SELECT i.prod_id, p.prod_name
   FROM inventory i JOIN products p ON i.prod_id = p.id;

Para expresar esto en lenguaje sencillo, lo que sucede es que primero verifico qué productos están en pedido y cuáles han sido entregados, y luego combino este conjunto de productos con todos los productos en el inventario. Cualquier producto que no esté en el conjunto de resultados no está en el inventario pero podría estar bajo pedido o podría haber sido entregado, pero no ambos.

Pero ahora, expresemos esto de otra manera, y veamos qué sucede. Quiero una lista de todos los productos que están en stock o han sido entregados y están en orden. El SQL sería entonces algo como esto, similar al SQL anterior pero ligeramente diferente:

 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 deliveries d JOIN products p ON d.prod_id = p.id;

¿Cómo interpretas esto entonces? ¿Enumera los productos que están en stock y que están bajo pedido y los productos que se están entregando? Esto es lo que parece, ¿verdad? Es solo que INTERSECT (y EXCEPT para el caso) tiene precedencia sobre UNION . Las dos declaraciones SQL producen el mismo conjunto de resultados, al menos en MariaDB y así es como el Estándar SQL dice que deberían funcionar las cosas. Pero hay una excepción, Oracle.

Cómo funciona esto en Oracle

Oracle ha tenido los cuatro operadores de conjuntos de SQL (UNION , UNION ALL , INTERSECT y EXCEPT ) durante mucho tiempo, mucho antes de que se estandarizaran, por lo que su implementación es un poco diferente. Probemos con las tablas de arriba e insertemos algunos datos en ellas. Los datos son muy simples y reflejan una empresa no tan exitosa, pero funcionan como un ejemplo, y aquí solo mostramos las columnas relevantes.

Tabla productos pedir_artículos inventario entregas
Columna prod_id nombre_producto id_pedido prod_id prod_id prod_id
Datos 1 Jarrón Azul 1 1 1 2
2 Jarrón Rojo 2 1 2 3
3 Alfombra Roja 2 3

Con los datos en su lugar, echemos un vistazo a la última declaración SQL anterior nuevamente. Hay una característica que le permite controlar la precedencia, y es usar paréntesis o corchetes (Introducido en MariaDB 10.4, vea https://jira.mariadb.org/browse/MDEV-11953), y usarlos para ilustrar lo que está pasando, la declaración se vería así:

 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 deliveries d JOIN products p ON d.prod_id = p.id);
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       1 | Vase Blue  |
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 3 rows in set (0.001 sec)

Ahora, usemos la misma técnica para hacer cumplir los tres componentes de la consulta para operar en orden estricto:

 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 deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 2 rows in set (0.001 sec)

Y por último sin paréntesis:

 MariaDB [test]> 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 deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       1 | Vase Blue  |
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 3 rows in set (0.001 sec)

Vemos que MariaDB, siguiendo el estándar, asumió que INTERSECT tiene prioridad sobre UNION . Lo que nos lleva a Oracle. Probemos el SQL anterior en Oracle usando sqlplus:

 SQL> SELECT i.prod_id, p.prod_name
   2   FROM inventory i JOIN products p ON i.prod_id = p.id
   3  UNION
   4  SELECT oi.prod_id, p.prod_name
   5   FROM order_items oi JOIN products p ON oi.prod_id = p.id
   6  INTERSECT
   7  SELECT d.prod_id, p.prod_name
   8   FROM deliveries d JOIN products p ON d.prod_id = p.id;
 
    PROD_ID PROD_NAME
 ---------- ------------------------------
          2 Vase Red
          3 Carpet Red

¿Qué está pasando aquí, te preguntarás? Bueno, Oracle no sigue el estándar. Los diferentes operadores de conjuntos se tratan como iguales y ninguno tiene precedencia sobre el otro. Este es un problema cuando está migrando aplicaciones de Oracle a MariaDB, y lo que es peor, es que esta diferencia es un poco difícil de encontrar. No se produce ningún error y se devuelven datos y, en muchos casos, se devuelven los datos correctos. Pero, en algunos casos, donde los datos son como en nuestro ejemplo anterior, se devuelven datos incorrectos, lo cual es un problema.

Efecto en la migración de datos

Entonces, ¿cómo lidiamos con esto si estamos migrando una aplicación de Oracle a MariaDB? Hay algunas opciones:

  • Reescriba la aplicación para que asuma que los datos devueltos por una consulta como esta están en línea con SQL Standard y MariaDB.
  • Reescriba las sentencias SQL, utilizando corchetes, para que MariaDB devuelva los mismos datos que Oracle
  • O, y esta es la forma más inteligente, use MariaDB SQL_MODE=Oracle ajuste.

Para la última y más inteligente forma de trabajar, necesitamos ejecutar MariaDB 10.3.7 o superior (esto fue sugerido por su servidor en https://jira.mariadb.org/browse/MDEV-13695). Veamos cómo funciona esto. Compara el resultado de este SELECT con el de Oracle arriba (que produce el mismo resultado) y el de MariaDB arriba (que no):

 MariaDB> set SQL_MODE=Oracle;
 Query OK, 0 rows affected (0.001 sec)
 
 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 deliveries d JOIN products p ON d.prod_id = p.id;
 +---------+------------+
 | prod_id | prod_name  |
 +---------+------------+
 |       2 | Vase Red   |
 |       3 | Carpet Red |
 +---------+------------+
 2 rows in set (0.002 sec)

Como puede ver, cuando SQL_MODE está configurado para Oracle , MariaDB realmente se comporta como Oracle. Esto no es lo único que SQL_MODE=Oracle lo hace, obviamente, pero es una de las áreas menos conocidas.

Conclusión

Los operadores de conjuntos INTERSECT y EXCEPT no se usan mucho, aunque aparecen aquí y allá, y hay algunos usos para ellos. Los ejemplos de este blog sirven más para ilustrar cómo funcionan estos operadores que para mostrarles un uso realmente bueno. Probablemente haya mejores ejemplos. Sin embargo, cuando está migrando de Oracle a MariaDB, los operadores de conjunto de SQL son realmente útiles ya que muchas aplicaciones de Oracle los usan y, como puede verse, se puede engañar a MariaDB para que funcione como Oracle, que no es estándar pero aún tiene un propósito. Pero, por defecto, MariaDB funciona como debería y sigue el estándar SQL.

Feliz SQL'ing
/Karlsson