Consulta SQL devuelve datos de múltiples tablas

434

Me gustaría saber lo siguiente:

  • ¿Cómo obtener datos de varias tablas en mi base de datos?
  • ¿Qué tipos de métodos hay para hacer esto?
  • ¿Qué son las uniones y los sindicatos y en qué se diferencian entre sí?
  • ¿Cuándo debo usar cada uno en comparación con los demás?

Estoy planeando usar esto en mi aplicación (por ejemplo, PHP), pero no quiero ejecutar múltiples consultas en la base de datos, ¿qué opciones tengo para obtener datos de varias tablas en una sola consulta?

Nota: Estoy escribiendo esto, ya que me gustaría poder vincular a una guía bien escrita sobre las numerosas preguntas que constantemente encuentro en la cola de PHP, por lo que puedo vincular esto para obtener más detalles cuando publico una respuesta.

Las respuestas cubren lo siguiente:

  1. Parte 1 - Uniones y Uniones
  2. Parte 2 - Subconsultas
  3. Parte 3 - Trucos y código eficiente
  4. Parte 4 - Subconsultas en la cláusula From
  5. Parte 5 - Bolsa mixta de trucos de John
Fluffeh
fuente

Respuestas:

469

Parte 1 - Uniones y Uniones

Esta respuesta cubre:

  1. Parte 1
  2. Parte 2
    • Subconsultas: qué son, dónde se pueden usar y a qué vigilar
    • Cartesian se une a AKA - ¡Oh, la miseria!

Hay varias formas de recuperar datos de varias tablas en una base de datos. En esta respuesta, usaré la sintaxis de unión ANSI-92. Esto puede ser diferente a una serie de otros tutoriales que usan la sintaxis ANSI-89 anterior (y si estás acostumbrado a 89, puede parecer mucho menos intuitivo, pero todo lo que puedo decir es que lo pruebes) ya que es mucho más fácil entender cuándo las consultas comienzan a ser más complejas. ¿Por qué usarlo? ¿Hay un aumento de rendimiento? La respuesta corta es no, pero es más fácil de leer una vez que te acostumbras. Es más fácil leer consultas escritas por otras personas que usan esta sintaxis.

También voy a utilizar el concepto de un pequeño caryard que tiene una base de datos para realizar un seguimiento de los automóviles que tiene disponibles. El propietario lo ha contratado como su chico de informática y espera que pueda soltarle los datos que solicita en un abrir y cerrar de ojos.

He hecho una serie de tablas de búsqueda que serán utilizadas por la tabla final. Esto nos dará un modelo razonable para trabajar. Para comenzar, ejecutaré mis consultas en una base de datos de ejemplo que tiene la siguiente estructura. Trataré de pensar en los errores comunes que se cometen al comenzar y explicaré qué sucede con ellos, así como, por supuesto, mostraré cómo corregirlos.

La primera tabla es simplemente una lista de colores para que sepamos qué colores tenemos en el patio del automóvil.

mysql> create table colors(id int(3) not null auto_increment primary key, 
    -> color varchar(15), paint varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> insert into colors (color, paint) values ('Red', 'Metallic'), 
    -> ('Green', 'Gloss'), ('Blue', 'Metallic'), 
    -> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from colors;
+----+-------+----------+
| id | color | paint    |
+----+-------+----------+
|  1 | Red   | Metallic |
|  2 | Green | Gloss    |
|  3 | Blue  | Metallic |
|  4 | White | Gloss    |
|  5 | Black | Gloss    |
+----+-------+----------+
5 rows in set (0.00 sec)

La tabla de marcas identifica las diferentes marcas de los automóviles que Caryard podría vender.

mysql> create table brands (id int(3) not null auto_increment primary key, 
    -> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| brand | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> insert into brands (brand) values ('Ford'), ('Toyota'), 
    -> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from brands;
+----+--------+
| id | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  3 | Nissan |
|  4 | Smart  |
|  5 | BMW    |
+----+--------+
5 rows in set (0.00 sec)

La tabla de modelos cubrirá diferentes tipos de automóviles, será más simple para esto usar diferentes tipos de automóviles en lugar de modelos de automóviles reales.

mysql> create table models (id int(3) not null auto_increment primary key, 
    -> model varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| model | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from models;
+----+--------+
| id | model  |
+----+--------+
|  1 | Sports |
|  2 | Sedan  |
|  3 | 4WD    |
|  4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)

Y finalmente, para unir todas estas otras tablas, la tabla que une todo. El campo ID es en realidad el número de lote único utilizado para identificar automóviles.

mysql> create table cars (id int(3) not null auto_increment primary key, 
    -> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type   | Null | Key | Default | Extra          |
+-------+--------+------+-----+---------+----------------+
| id    | int(3) | NO   | PRI | NULL    | auto_increment |
| color | int(3) | YES  |     | NULL    |                |
| brand | int(3) | YES  |     | NULL    |                |
| model | int(3) | YES  |     | NULL    |                |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1), 
    -> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
|  1 |     1 |     2 |     1 |
|  2 |     3 |     1 |     2 |
|  3 |     5 |     3 |     1 |
|  4 |     4 |     4 |     2 |
|  5 |     2 |     2 |     3 |
|  6 |     3 |     5 |     4 |
|  7 |     4 |     1 |     3 |
|  8 |     2 |     2 |     1 |
|  9 |     5 |     2 |     3 |
| 10 |     4 |     5 |     1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)

Esto nos dará suficientes datos (espero) para cubrir los siguientes ejemplos de diferentes tipos de combinaciones y también proporcionará suficientes datos para que valgan la pena.

Entonces, entrando en la arena, el jefe quiere saber las identificaciones de todos los autos deportivos que tiene .

Esta es una combinación simple de dos tablas. Tenemos una tabla que identifica el modelo y la tabla con el stock disponible. Como puede ver, los datos en la modelcolumna de la carstabla se relacionan con la modelscolumna de la carstabla que tenemos. Ahora, sabemos que la tabla de modelos tiene un ID de 1for, Sportsasí que vamos a escribir la unión.

select
    ID,
    model
from
    cars
        join models
            on model=ID

Entonces esta consulta se ve bien, ¿verdad? Hemos identificado las dos tablas y contiene la información que necesitamos y utilizamos una combinación que identifica correctamente en qué columnas unirnos.

ERROR 1052 (23000): Column 'ID' in field list is ambiguous

Oh no! Un error en nuestra primera consulta! Sí, y es una ciruela. Verá, la consulta tiene las columnas correctas, pero algunas de ellas existen en ambas tablas, por lo que la base de datos se confunde acerca de qué columna real queremos decir y dónde. Hay dos soluciones para resolver esto. El primero es agradable y simple, podemos usar tableName.columnNamepara decirle a la base de datos exactamente lo que queremos decir, así:

select
    cars.ID,
    models.model
from
    cars
        join models
            on cars.model=models.ID

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
|  2 | Sedan  |
|  4 | Sedan  |
|  5 | 4WD    |
|  7 | 4WD    |
|  9 | 4WD    |
|  6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)

El otro probablemente se usa con más frecuencia y se llama alias de tabla. Las tablas en este ejemplo tienen nombres simples y agradables, pero escribir algo así KPI_DAILY_SALES_BY_DEPARTMENTprobablemente envejecería rápidamente, por lo que una forma simple es ponerle un apodo a la tabla de esta manera:

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID

Ahora, de vuelta a la solicitud. Como puede ver, tenemos la información que necesitamos, pero también tenemos información que no se solicitó, por lo que debemos incluir una cláusula where en la declaración para obtener solo los autos deportivos como se solicitó. Como prefiero el método de alias de tabla en lugar de usar los nombres de tabla una y otra vez, me atendré a él a partir de este momento.

Claramente, necesitamos agregar una cláusula where a nuestra consulta. Podemos identificar autos deportivos por ID=1o model='Sports'. A medida que la ID se indexa y la clave principal (y resulta que está escribiendo menos), usemos eso en nuestra consulta.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

¡Bingo! El jefe esta feliz. Por supuesto, como jefe y nunca contento con lo que pidió, mira la información y luego dice que también quiero los colores .

Bien, tenemos una buena parte de nuestra consulta ya escrita, pero necesitamos usar una tercera tabla que es colores. Ahora, nuestra tabla de información principal carsalmacena la identificación del color del automóvil y esta vuelve a la columna de identificación del color. Entonces, de manera similar al original, podemos unirnos a una tercera tabla:

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

Maldición, aunque la tabla se unió correctamente y las columnas relacionadas se vincularon, olvidamos extraer la información real de la nueva tabla que acabamos de vincular.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)

Correcto, ese es el jefe de nuestras espaldas por un momento. Ahora, para explicar algo de esto con un poco más de detalle. Como puede ver, la fromcláusula en nuestra declaración vincula nuestra tabla principal (a menudo uso una tabla que contiene información en lugar de una tabla de búsqueda o dimensión. La consulta funcionaría igual de bien con todas las tablas cambiadas, pero tiene menos sentido cuando volvemos a esta consulta para leerla dentro de unos meses, por lo que a menudo es mejor intentar escribir una consulta que sea agradable y fácil de entender: exponga intuitivamente, use una sangría agradable para que todo sea tan claro como sea posible. puede ser. Si continúa enseñando a otros, intente inculcar estas características en sus consultas, especialmente si va a solucionarlos.

Es completamente posible seguir vinculando más y más tablas de esta manera.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Si bien olvidé incluir una tabla donde podríamos unir más de una columna en la joindeclaración, aquí hay un ejemplo. Si la modelstabla tenía modelos específicos de la marca y, por lo tanto, también tenía una columna llamada brandque se vinculaba de nuevo a la brandstabla en el IDcampo, podría hacerse así:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
            and b.brand=d.ID
where
    b.ID=1

Puede ver que la consulta anterior no solo vincula las tablas unidas a la carstabla principal , sino que también especifica las uniones entre las tablas ya unidas. Si esto no se hizo, el resultado se llama una unión cartesiana, que es hablar mal. Una combinación cartesiana es aquella en la que se devuelven filas porque la información no le dice a la base de datos cómo limitar los resultados, por lo que la consulta devuelve todas las filas que se ajustan a los criterios.

Entonces, para dar un ejemplo de una unión cartesiana, ejecutemos la siguiente consulta:

select
    a.ID,
    b.model
from
    cars a
        join models b

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  1 | Sedan  |
|  1 | 4WD    |
|  1 | Luxury |
|  2 | Sports |
|  2 | Sedan  |
|  2 | 4WD    |
|  2 | Luxury |
|  3 | Sports |
|  3 | Sedan  |
|  3 | 4WD    |
|  3 | Luxury |
|  4 | Sports |
|  4 | Sedan  |
|  4 | 4WD    |
|  4 | Luxury |
|  5 | Sports |
|  5 | Sedan  |
|  5 | 4WD    |
|  5 | Luxury |
|  6 | Sports |
|  6 | Sedan  |
|  6 | 4WD    |
|  6 | Luxury |
|  7 | Sports |
|  7 | Sedan  |
|  7 | 4WD    |
|  7 | Luxury |
|  8 | Sports |
|  8 | Sedan  |
|  8 | 4WD    |
|  8 | Luxury |
|  9 | Sports |
|  9 | Sedan  |
|  9 | 4WD    |
|  9 | Luxury |
| 10 | Sports |
| 10 | Sedan  |
| 10 | 4WD    |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)

Dios mío, eso es feo. Sin embargo, en lo que respecta a la base de datos, es exactamente lo que se solicitó. En la consulta, solicitamos el IDde carsy el modelde models. Sin embargo, debido a que no especificamos cómo unir las tablas, la base de datos ha coincidido con cada fila de la primera tabla con cada fila de la segunda tabla.

Bien, entonces el jefe está de regreso y quiere más información nuevamente. Quiero la misma lista, pero también incluyo 4WD en ella .

Sin embargo, esto nos da una gran excusa para ver dos formas diferentes de lograr esto. Podríamos agregar otra condición a la cláusula where como esta:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
    or b.ID=3

Si bien lo anterior funcionará perfectamente bien, veámoslo de manera diferente, esta es una gran excusa para mostrar cómo funcionará una unionconsulta.

Sabemos que lo siguiente devolverá todos los autos deportivos:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Y lo siguiente devolvería todos los 4WD:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

Entonces, al agregar una union allcláusula entre ellos, los resultados de la segunda consulta se agregarán a los resultados de la primera consulta.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
union all
select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
|  5 | 4WD    | Green |
|  7 | 4WD    | White |
|  9 | 4WD    | Black |
+----+--------+-------+
7 rows in set (0.00 sec)

Como puede ver, los resultados de la primera consulta se devuelven primero, seguidos de los resultados de la segunda consulta.

En este ejemplo, por supuesto, habría sido mucho más fácil simplemente usar la primera consulta, pero las unionconsultas pueden ser excelentes para casos específicos. Ellos son una gran manera para obtener resultados específicos de las tablas de las tablas que no están unidos entre sí con facilidad - o para el caso por completo tablas no relacionadas. Sin embargo, hay algunas reglas a seguir.

  • Los tipos de columna de la primera consulta deben coincidir con los tipos de columna de cualquier otra consulta a continuación.
  • Los nombres de las columnas de la primera consulta se utilizarán para identificar el conjunto completo de resultados.
  • El número de columnas en cada consulta debe ser el mismo.

Ahora, puede que se pregunte cuál es la diferencia entre usar uniony union all. Una unionconsulta eliminará duplicados, mientras que una union allno lo hará. Esto significa que hay un pequeño impacto en el rendimiento cuando se usa unionmás, union allpero los resultados pueden valer la pena; sin embargo, no especularé sobre ese tipo de cosas en esto.

En esta nota, podría valer la pena señalar algunas notas adicionales aquí.

  • Si quisiéramos ordenar los resultados, podemos usar un order bypero ya no puede usar el alias. En la consulta anterior, agregar un order by a.IDdaría como resultado un error, en lo que respecta a los resultados, la columna se llama en IDlugar de hacerlo a.ID, a pesar de que se ha utilizado el mismo alias en ambas consultas.
  • Solo podemos tener una order bydeclaración, y debe ser como la última declaración.

Para los siguientes ejemplos, agregaré algunas filas adicionales a nuestras tablas.

He agregado Holdena la tabla de marcas. También he agregado una fila carsque tiene el colorvalor de 12, que no tiene referencia en la tabla de colores.

De acuerdo, el jefe está de regreso otra vez, ladrando solicitudes - * ¡Quiero un recuento de cada marca que tenemos y la cantidad de autos que contiene! `- Típico, solo llegamos a una sección interesante de nuestra discusión y el jefe quiere más trabajo .

Rightyo, así que lo primero que debemos hacer es obtener una lista completa de posibles marcas.

select
    a.brand
from
    brands a

+--------+
| brand  |
+--------+
| Ford   |
| Toyota |
| Nissan |
| Smart  |
| BMW    |
| Holden |
+--------+
6 rows in set (0.00 sec)

Ahora, cuando unimos esto a nuestra mesa de autos, obtenemos el siguiente resultado:

select
    a.brand
from
    brands a
        join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Nissan |
| Smart  |
| Toyota |
+--------+
5 rows in set (0.00 sec)

Lo cual, por supuesto, es un problema: no estamos viendo ninguna mención de la hermosa Holdenmarca que agregué.

Esto se debe a que una unión busca filas coincidentes en ambas tablas. Como no hay datos en los automóviles que sean de tipo Holden, no se devuelven. Aquí es donde podemos usar una outerunión. Esto devolverá todos los resultados de una tabla, ya sea que coincidan en la otra tabla o no:

select
    a.brand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Holden |
| Nissan |
| Smart  |
| Toyota |
+--------+
6 rows in set (0.00 sec)

Ahora que tenemos eso, podemos agregar una función agregada encantadora para obtener un recuento y sacar al jefe de nuestras espaldas por un momento.

select
    a.brand,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+--------------+
| brand  | countOfBrand |
+--------+--------------+
| BMW    |            2 |
| Ford   |            2 |
| Holden |            0 |
| Nissan |            1 |
| Smart  |            1 |
| Toyota |            5 |
+--------+--------------+
6 rows in set (0.00 sec)

Y con eso, el jefe se esconde.

Ahora, para explicar esto con más detalle, las uniones externas pueden ser del tipo lefto right. La izquierda o la derecha define qué tabla está completamente incluida. A left outer joinincluirá todas las filas de la tabla de la izquierda, mientras que (lo adivinó) a right outer jointrae todos los resultados de la tabla de la derecha a los resultados.

Algunas bases de datos permitirán una full outer joinque traerá resultados (ya sea coincidentes o no) de ambas tablas, pero esto no es compatible con todas las bases de datos.

Ahora, probablemente supongo que en este momento, se está preguntando si puede fusionar o no los tipos de combinación en una consulta, y la respuesta es sí, absolutamente puede.

select
    b.brand,
    c.color,
    count(a.id) as countOfBrand
from
    cars a
        right outer join brands b
            on b.ID=a.brand
        join colors c
            on a.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)

Entonces, ¿por qué no son los resultados que se esperaban? Esto se debe a que, aunque hemos seleccionado la combinación externa de automóviles a marcas, no se especificó en la combinación de colores, por lo que esa combinación en particular solo devolverá resultados que coincidan en ambas tablas.

Aquí está la consulta que funcionaría para obtener los resultados que esperábamos:

select
    a.brand,
    c.color,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
        left outer join colors c
            on b.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Holden | NULL  |            0 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| Toyota | NULL  |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)

Como podemos ver, tenemos dos combinaciones externas en la consulta y los resultados se obtienen como se esperaba.

Ahora, ¿qué tal esos otros tipos de combinaciones que preguntas? ¿Qué pasa con las intersecciones?

Bueno, no todas las bases de datos son compatibles, intersectionpero prácticamente todas las bases de datos le permitirán crear una intersección a través de una unión (o una declaración where bien estructurada como mínimo).

Una intersección es un tipo de unión algo similar a la uniondescrita anteriormente, pero la diferencia es que solo devuelve filas de datos que son idénticos (y quiero decir idénticos) entre las diversas consultas individuales unidas por la unión. Solo se devolverán las filas que sean idénticas en todos los aspectos.

Un ejemplo simple sería como tal:

select
    *
from
    colors
where
    ID>2
intersect
select
    *
from
    colors
where
    id<4

Mientras que una unionconsulta normal devolvería todas las filas de la tabla (la primera consulta devolvería cualquier cosa ID>2y la segunda cualquier cosa que tuviera ID<4) lo que daría como resultado un conjunto completo, una consulta de intersección solo devolvería la coincidencia de fila id=3ya que cumple con ambos criterios.

Ahora, si su base de datos no admite una intersectconsulta, lo anterior se puede cumplir fácilmente con la siguiente consulta:

select
    a.ID,
    a.color,
    a.paint
from
    colors a
        join colors b
            on a.ID=b.ID
where
    a.ID>2
    and b.ID<4

+----+-------+----------+
| ID | color | paint    |
+----+-------+----------+
|  3 | Blue  | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)

Si desea realizar una intersección en dos tablas diferentes utilizando una base de datos que no admite inherentemente una consulta de intersección, deberá crear una unión en cada columna de las tablas.

Fluffeh
fuente
2
@Fluffeh Nice respuestas. Tengo una sugerencia: si desea convertirlo en un tutorial SQL asesino, solo le faltará agregar diagramas de Venn; Entendí de inmediato izquierda y derecha se une gracias a ellos. Solicitud personal: ¿Tiene algún tutorial sobre errores comunes / ajuste de rendimiento?
StrayChild01
25
Oh mi. Mi rueda de desplazamiento está rota. Gran pregunta y respuesta. Desearía poder votar esto 10 veces.
Amal Murali
3
Jeje, gracias por los comentarios positivos. Sin embargo, sigue desplazándote, esta fue solo la primera respuesta. SO dijo que mi respuesta era demasiado larga para encajar en una "respuesta", así que tuve que usar algunas :)
Fluffeh
77
Honestamente, creo que esta respuesta debe acortarse un poco.
einpoklum
Excelente articulo. Base de datos se une a 101.
maqs
101

Ok, esta publicación me pareció muy interesante y me gustaría compartir algunos de mis conocimientos sobre cómo crear una consulta. Gracias por este Fluffeh . Otros que pueden leer esto y pueden sentir que estoy equivocado son 101% libres de editar y criticar mi respuesta. ( Honestamente, me siento muy agradecido por corregir mi error (s). )

Publicaré algunas de las preguntas frecuentes en la MySQLetiqueta.


Truco No. 1 ( filas que coinciden con múltiples condiciones )

Dado este esquema

CREATE TABLE MovieList
(
    ID INT,
    MovieName VARCHAR(25),
    CONSTRAINT ml_pk PRIMARY KEY (ID),
    CONSTRAINT ml_uq UNIQUE (MovieName)
);

INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');

CREATE TABLE CategoryList
(
    MovieID INT,
    CategoryName VARCHAR(25),
    CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
    CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);

INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');

PREGUNTA

Encuentra todas las películas que pertenecen a por lo menos tanto Comedy y Romancecategorías.

Solución

Esta pregunta puede ser muy complicada a veces. Puede parecer que una consulta como esta será la respuesta:

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName = 'Comedy' AND
        b.CategoryName = 'Romance'

Demostración de SQLFiddle

lo cual definitivamente está muy mal porque no produce ningún resultado . La explicación de esto es que solo hay un valor válido de CategoryNameen cada fila . Por ejemplo, la primera condición devuelve verdadero , la segunda condición siempre es falsa. Por lo tanto, al usar el ANDoperador, ambas condiciones deben ser verdaderas; de lo contrario, será falso. Otra consulta es así,

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')

Demostración de SQLFiddle

y el resultado sigue siendo incorrecto porque coincide con el registro que tiene al menos una coincidencia en el categoryName. La solución real sería contar el número de instancias de grabación por película . El número de instancias debe coincidir con el número total de los valores suministrados en la condición.

SELECT  a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2

SQLFiddle Demo (la respuesta)


Truco No. 2 ( registro máximo para cada entrada )

Esquema dado

CREATE TABLE Software
(
    ID INT,
    SoftwareName VARCHAR(25),
    Descriptions VARCHAR(150),
    CONSTRAINT sw_pk PRIMARY KEY (ID),
    CONSTRAINT sw_uq UNIQUE (SoftwareName)  
);

INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');

CREATE TABLE VersionList
(
    SoftwareID INT,
    VersionNo INT,
    DateReleased DATE,
    CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
    CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);

INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');

PREGUNTA

Encuentra la última versión de cada software. Visualizar las siguientes columnas: SoftwareName, Descriptions, LatestVersion( de la columna VersionNo ),DateReleased

Solución

Algunos desarrolladores de SQL utilizan por error MAX()la función agregada. Tienden a crear así,

SELECT  a.SoftwareName, a.Descriptions,
        MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM    Software a
        INNER JOIN VersionList b
            ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID

Demostración de SQLFiddle

(la mayoría de RDBMS genera un error de sintaxis en esto debido a que no especifica algunas de las columnas no agregadas en la group bycláusula ) el resultado produce el correcto LatestVersionen cada software pero obviamente DateReleasedes incorrecto. MySQLno es compatible Window Functionsy, Common Table Expressionsin embargo, como algunos RDBMS ya lo hacen. La solución a este problema es crear una subqueryque obtenga el máximo individual versionNoen cada software y luego se una en las otras tablas.

SELECT  a.SoftwareName, a.Descriptions,
        b.LatestVersion, c.DateReleased
FROM    Software a
        INNER JOIN
        (
            SELECT  SoftwareID, MAX(VersionNO) LatestVersion
            FROM    VersionList
            GROUP BY SoftwareID
        ) b ON a.ID = b.SoftwareID
        INNER JOIN VersionList c
            ON  c.SoftwareID = b.SoftwareID AND
                c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle Demo (la respuesta)


Así que eso fue todo. Publicaré otro pronto cuando recuerde cualquier otra pregunta frecuente en la MySQLetiqueta. Gracias por leer este pequeño artículo. Espero que al menos tengas un poco de conocimiento de esto.

ACTUALIZACIÓN 1


Truco No. 3 ( Encontrar el último registro entre dos ID )

Esquema dado

CREATE TABLE userList
(
    ID INT,
    NAME VARCHAR(20),
    CONSTRAINT us_pk PRIMARY KEY (ID),
    CONSTRAINT us_uq UNIQUE (NAME)  
);

INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');

CREATE TABLE CONVERSATION
(
    ID INT,
    FROM_ID INT,
    TO_ID INT,
    MESSAGE VARCHAR(250),
    DeliveryDate DATE
);

INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');

PREGUNTA

Encuentra la última conversación entre dos usuarios.

Solución

SELECT    b.Name SenderName,
          c.Name RecipientName,
          a.Message,
          a.DeliveryDate
FROM      Conversation a
          INNER JOIN userList b
            ON a.From_ID = b.ID
          INNER JOIN userList c
            ON a.To_ID = c.ID
WHERE     (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
    SELECT  LEAST(FROM_ID, TO_ID) minFROM,
            GREATEST(FROM_ID, TO_ID) maxTo,
            MAX(DeliveryDate) maxDate
    FROM    Conversation
    GROUP BY minFROM, maxTo
)

Demostración de SQLFiddle

John Woo
fuente
¡Increíble! Una advertencia, John, su primera solución funciona solo porque hay una restricción única en los dos campos. Podría haber usado una solución más general para ayudar con un problema común. En mi opinión, la única solución es hacer selecciones individuales para comedyy romance. Havingno se adapta entonces ..
nawfal
@nawfal no realmente, si no se agregó una restricción única, entonces debe agregar distinctla cláusula have SQLFiddle Demo : D
John Woo
63

Parte 2 - Subconsultas

Bien, ahora el jefe ha vuelto a entrar: ¡quiero una lista de todos nuestros autos con la marca y un total de cuántos de esa marca tenemos!

Esta es una gran oportunidad para usar el siguiente truco en nuestra bolsa de productos SQL: la subconsulta. Si no está familiarizado con el término, una subconsulta es una consulta que se ejecuta dentro de otra consulta. Hay muchas formas diferentes de usarlos.

Para nuestra solicitud, primero hagamos una consulta simple que enumere cada automóvil y la marca:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID

Ahora, si simplemente quisiéramos obtener un recuento de autos ordenados por marca, por supuesto podríamos escribir esto:

select
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    b.brand

+--------+-----------+
| brand  | countCars |
+--------+-----------+
| BMW    |         2 |
| Ford   |         2 |
| Nissan |         1 |
| Smart  |         1 |
| Toyota |         5 |
+--------+-----------+

Entonces, deberíamos poder simplemente agregar la función de conteo a nuestra consulta original, ¿verdad?

select
    a.ID,
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    a.ID,
    b.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         1 |
|  2 | Ford   |         1 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         1 |
|  6 | BMW    |         1 |
|  7 | Ford   |         1 |
|  8 | Toyota |         1 |
|  9 | Toyota |         1 |
| 10 | BMW    |         1 |
| 11 | Toyota |         1 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Lamentablemente, no, no podemos hacer eso. La razón es que cuando agregamos la ID del automóvil (columna a.ID) tenemos que agregarla al grupo, así que ahora, cuando funciona la función de conteo, solo hay una ID coincidente por ID.

Sin embargo, aquí es donde podemos usar una subconsulta; de hecho, podemos hacer dos tipos de subconsulta completamente diferentes que arrojarán los mismos resultados que necesitamos para esto. El primero es simplemente poner la subconsulta en la selectcláusula. Esto significa que cada vez que obtenemos una fila de datos, la subconsulta se ejecutará, obtendrá una columna de datos y luego aparecerá en nuestra fila de datos.

select
    a.ID,
    b.brand,
    (
    select
        count(c.ID)
    from
        cars c
    where
        a.brand=c.brand
    ) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  2 | Ford   |         2 |
|  7 | Ford   |         2 |
|  1 | Toyota |         5 |
|  5 | Toyota |         5 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 11 | Toyota |         5 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  6 | BMW    |         2 |
| 10 | BMW    |         2 |
+----+--------+-----------+
11 rows in set (0.00 sec)

¡Y Bam !, esto nos haría bien. Sin embargo, si se dio cuenta, esta subconsulta tendrá que ejecutarse para cada fila de datos que devolvemos. Incluso en este pequeño ejemplo, solo tenemos cinco marcas diferentes de automóviles, pero la subconsulta se ejecutó once veces ya que tenemos once filas de datos que estamos devolviendo. Entonces, en este caso, no parece ser la forma más eficiente de escribir código.

Para un enfoque diferente, ejecutemos una subconsulta y pretendamos que es una tabla:

select
    a.ID,
    b.brand,
    d.countCars
from
    cars a
        join brands b
            on a.brand=b.ID
        join
            (
            select
                c.brand,
                count(c.ID) as countCars
            from
                cars c
            group by
                c.brand
            ) d
            on a.brand=d.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         5 |
|  2 | Ford   |         2 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         5 |
|  6 | BMW    |         2 |
|  7 | Ford   |         2 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 10 | BMW    |         2 |
| 11 | Toyota |         5 |
+----+--------+-----------+
11 rows in set (0.00 sec)

De acuerdo, tenemos los mismos resultados (ordenados ligeramente diferentes, parece que la base de datos quería devolver los resultados ordenados por la primera columna que seleccionamos esta vez), pero los mismos números correctos.

Entonces, ¿cuál es la diferencia entre los dos, y cuándo debemos usar cada tipo de subconsulta? Primero, asegurémonos de que entendemos cómo funciona esa segunda consulta. Seleccionamos dos tablas en la fromcláusula de nuestra consulta, y luego escribimos una consulta y le dijimos a la base de datos que en realidad era una tabla, con lo cual la base de datos está perfectamente satisfecha. No puede haber algunos beneficios al uso de este método (así como algunas limitaciones). Lo principal es que esta subconsulta se ejecutó una vez . Si nuestra base de datos contuviera un gran volumen de datos, bien podría haber una mejora masiva sobre el primer método. Sin embargo, como estamos usando esto como una tabla, tenemos que incorporar filas adicionales de datos, para que puedan unirse de nuevo a nuestras filas de datos. También tenemos que asegurarnos de que haya suficientesfilas de datos si vamos a usar una unión simple como en la consulta anterior. Si recuerda, la unión solo retirará las filas que tienen datos coincidentes en ambos lados de la unión. Si no tenemos cuidado, esto podría resultar en que no se devuelvan datos válidos de nuestra tabla de autos si no hubiera una fila coincidente en esta subconsulta.

Ahora, mirando hacia atrás en la primera subconsulta, también hay algunas limitaciones. Debido a que estamos volviendo a colocar los datos en una sola fila, SOLO podemos retirar una fila de datos. Subconsultas utilizados en la selectcláusula de una consulta muy a menudo utilizan sólo una función de agregación tales como sum, count, maxu otra función de agregado similar. No tienen que hacerlo, pero a menudo así es como se escriben.

Entonces, antes de continuar, echemos un vistazo rápido a dónde más podemos usar una subconsulta. Podemos usarlo en la wherecláusula: ahora, este ejemplo es un poco artificial, ya que en nuestra base de datos, hay mejores formas de obtener los siguientes datos, pero ya que es solo un ejemplo, echemos un vistazo:

select
    ID,
    brand
from
    brands
where
    brand like '%o%'

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  6 | Holden |
+----+--------+
3 rows in set (0.00 sec)

Esto nos devuelve una lista de ID de marca y nombres de marca (la segunda columna solo se agrega para mostrarnos las marcas) que contienen la letra oen el nombre.

Ahora, podríamos usar los resultados de esta consulta en una cláusula where:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in
        (
        select
            ID
        from
            brands
        where
            brand like '%o%'
        )

+----+--------+
| ID | brand  |
+----+--------+
|  2 | Ford   |
|  7 | Ford   |
|  1 | Toyota |
|  5 | Toyota |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Como puede ver, a pesar de que la subconsulta devolvía las tres identificaciones de marca, nuestra tabla de autos solo tenía entradas para dos de ellas.

En este caso, para más detalles, la subconsulta funciona como si escribiéramos el siguiente código:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in (1,2,6)

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Toyota |
|  2 | Ford   |
|  5 | Toyota |
|  7 | Ford   |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Nuevamente, puede ver cómo una subconsulta vs entradas manuales ha cambiado el orden de las filas al regresar de la base de datos.

Mientras discutimos las subconsultas, veamos qué más podemos hacer con una subconsulta:

  • Puede colocar una subconsulta dentro de otra subconsulta, y así sucesivamente. Hay un límite que depende de su base de datos, pero a falta de funciones recursivas de algún programador loco y maníaco, la mayoría de la gente nunca llegará a ese límite.
  • Puede colocar una serie de subconsultas en una sola consulta, algunas en la selectcláusula, algunas en la fromcláusula y un par más en la wherecláusula; solo recuerde que cada una de las que ingresa hace que su consulta sea más compleja y probablemente tome más tiempo para ejecutar.

Si necesita escribir un código eficiente, puede ser beneficioso escribir la consulta de varias maneras y ver (ya sea cronometrando o usando un plan de explicación) cuál es la consulta óptima para obtener sus resultados. La primera forma en que funciona puede no ser siempre la mejor manera de hacerlo.

Fluffeh
fuente
Muy importante para los nuevos desarrolladores: las subconsultas probablemente se ejecuten una vez para cada resultado a menos que pueda usar la subconsulta como una combinación (como se muestra arriba).
Xeoncross
59

Parte 3 - Trucos y código eficiente

MySQL en () eficiencia

Pensé que agregaría algunos bits adicionales, para consejos y trucos que han surgido.

Una pregunta que veo surgir un poco, es ¿Cómo obtengo filas no coincidentes de dos tablas y veo la respuesta más comúnmente aceptada como algo como lo siguiente (según nuestra tabla de autos y marcas, que tiene a Holden como marca, pero no aparece en la tabla de coches):

select
    a.ID,
    a.brand
from
    brands a
where
    a.ID not in(select brand from cars)

Y , funcionará.

+----+--------+
| ID | brand  |
+----+--------+
|  6 | Holden |
+----+--------+
1 row in set (0.00 sec)

Sin embargo, es no eficiente en alguna base de datos. Aquí hay un enlace a una pregunta sobre el desbordamiento de la pila , y aquí hay un excelente artículo en profundidad si desea entrar en el meollo de la cuestión.

La respuesta corta es, si el optimizador no lo maneja eficientemente, puede ser mucho mejor usar una consulta como la siguiente para obtener filas no coincidentes:

select
    a.brand
from
    brands a
        left join cars b
            on a.id=b.brand
where
    b.brand is null

+--------+
| brand  |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)

Actualizar tabla con la misma tabla en subconsulta

Ahhh, otro viejo pero bueno: el viejo No puede especificar 'marcas' de tabla de destino para actualizar en la cláusula FROM .

MySQL no le permitirá ejecutar una update...consulta con una subselección en la misma tabla. Ahora, usted podría estar pensando, ¿por qué no simplemente incluirlo en la cláusula where? Pero, ¿qué sucede si desea actualizar solo la fila con la max()fecha entre un montón de otras filas? No puedes hacer eso exactamente en una cláusula where.

update 
    brands 
set 
    brand='Holden' 
where 
    id=
        (select 
            id 
        from 
            brands 
        where 
            id=6);
ERROR 1093 (HY000): You can't specify target table 'brands' 
for update in FROM clause

Entonces, no podemos hacer eso, ¿eh? Bueno no exactamente. Existe una solución furtiva que desconoce un número sorprendentemente grande de usuarios, aunque incluye algunos hackers a los que deberá prestar atención.

Puede pegar la subconsulta dentro de otra subconsulta, lo que deja suficiente espacio entre las dos consultas para que funcione. Sin embargo, tenga en cuenta que puede ser más seguro pegar la consulta dentro de una transacción; esto evitará que se realicen otros cambios en las tablas mientras se ejecuta la consulta.

update 
    brands 
set 
    brand='Holden' 
where id=
    (select 
        id 
    from 
        (select 
            id 
        from 
            brands 
        where 
            id=6
        ) 
    as updateTable);

Query OK, 0 rows affected (0.02 sec)
Rows matched: 1  Changed: 0  Warnings: 0
Fluffeh
fuente
3
Solo quiero señalar que la construcción WHERE NOT EXISTS () es bastante idéntica desde un 'punto de vista de eficiencia' pero en mi opinión es mucho más fácil de leer / entender. Por otra parte, mi conocimiento se limita a MSSQL y no puedo jurar si lo mismo es cierto en otras plataformas.
deroby
Acabo de probar este tipo de comparación el otro día, donde NOT IN () tenía una lista de varios cientos de ID y no había diferencia entre él y la versión de combinación de la consulta. Tal vez hace la diferencia cuando subes a miles o miles de millones.
Buttle Butkus
18

Puede usar el concepto de consultas múltiples en la palabra clave FROM. Déjame mostrarte un ejemplo:

SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY    
FROM  (
          SELECT c.id cnty,l.name
          FROM   county c, location l
          WHERE  c.id=l.county_id AND l.end_Date IS NOT NULL
      ) c_loc, emp e 
      INNER JOIN dept d ON e.deptno =d.id
      LEFT JOIN 
      ( 
         SELECT l.id lappy, c.name cmpy
         FROM   laptop l, company c
         WHERE l.make = c.name
      ) lap ON e.cmpy_id=lap.cmpy

Puede usar tantas tablas como desee. Utilice uniones externas y uniones donde sea necesario, incluso dentro de subconsultas de tabla.

Es un método muy fácil para involucrar tantas tablas como campos.

prashant1988
fuente
8

Espera que esto lo haga encontrar las tablas mientras estás leyendo:

jsfiddle

mysql> show columns from colors;                                                         
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+           
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
Anton Chan
fuente