Consulta SQL retorna dados de várias tabelas

434

Eu gostaria de saber o seguinte:

  • como obter dados de várias tabelas no meu banco de dados?
  • que tipos de métodos existem para fazer isso?
  • o que são uniões e uniões e como elas são diferentes umas das outras?
  • Quando devo usar cada um comparado aos outros?

Estou planejando usar isso no meu aplicativo (por exemplo - PHP), mas não quero executar várias consultas no banco de dados, que opções tenho para obter dados de várias tabelas em uma única consulta?

Nota: Estou escrevendo isso, pois gostaria de poder vincular a um guia bem escrito sobre as inúmeras perguntas que sempre encontro na fila do PHP, para que eu possa vincular a isso para obter mais detalhes ao publicar uma resposta.

As respostas abrangem o seguinte:

  1. Parte 1 - Associações e Sindicatos
  2. Parte 2 - Subconsultas
  3. Parte 3 - Truques e código eficiente
  4. Parte 4 - Subconsultas na cláusula From
  5. Parte 5 - Misto de truques de John
Fluffeh
fonte

Respostas:

469

Parte 1 - Associações e Sindicatos

Esta resposta abrange:

  1. Parte 1
  2. Parte 2
    • Subconsultas - o que são, onde podem ser usadas e o que observar
    • Cartesian junta-se à AKA - Oh, a miséria!

Existem várias maneiras de recuperar dados de várias tabelas em um banco de dados. Nesta resposta, usarei a sintaxe de junção ANSI-92. Isso pode ser diferente de vários outros tutoriais por aí que usam a sintaxe ANSI-89 mais antiga (e se você está acostumado a 89, pode parecer muito menos intuitivo - mas tudo o que posso dizer é experimentá-lo), pois é muito mais fácil para entender quando as consultas começam a ficar mais complexas. Por que usar isso? Existe um ganho de desempenho? A resposta curta é não, mas é mais fácil ler quando você se acostumar. É mais fácil ler consultas escritas por outras pessoas usando esta sintaxe.

Também vou usar o conceito de um pequeno estacionamento que possui um banco de dados para acompanhar quais carros ele tem disponível. O proprietário contratou você como o responsável pelo computador de TI e espera que você possa enviar a ele os dados que ele solicita imediatamente.

Eu criei várias tabelas de pesquisa que serão usadas pela mesa final. Isso nos dará um modelo razoável para trabalhar. Para começar, executarei minhas consultas em um banco de dados de exemplo que possui a seguinte estrutura. Tentarei pensar nos erros comuns cometidos ao iniciar e explicarei o que há de errado com eles - bem como, é claro, mostrar como corrigi-los.

A primeira tabela é simplesmente uma lista de cores para que saibamos que cores temos no pátio de carros.

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)

A tabela de marcas identifica as diferentes marcas dos carros que o quintal poderia 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)

A tabela de modelos cobrirá diferentes tipos de carros; será mais simples usar diferentes tipos de carros do que modelos reais.

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)

E, finalmente, amarrar todas essas outras mesas, a mesa que une tudo. O campo ID é, na verdade, o número de lote exclusivo usado para identificar carros.

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)

Isso nos fornecerá dados suficientes (espero) para encobrir os exemplos abaixo de diferentes tipos de junções e também dados suficientes para fazê-los valer a pena.

Então, entrando no clima, o chefe quer saber as identificações de todos os carros esportivos que ele tem .

Esta é uma junção simples de duas tabelas. Temos uma tabela que identifica o modelo e a tabela com o estoque disponível. Como você pode ver, os dados na modelcoluna da carstabela estão relacionados à modelscoluna da carstabela que temos. Agora, sabemos que a tabela de modelos tem um ID 1para, Sportsentão vamos escrever a junção.

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

Então, essa consulta parece boa, certo? Identificamos as duas tabelas e contêm as informações necessárias e usamos uma junção que identifica corretamente em quais colunas participar.

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

Oh não! Um erro na nossa primeira consulta! Sim, e é uma ameixa. Veja bem, a consulta realmente possui as colunas corretas, mas algumas delas existem nas duas tabelas; portanto, o banco de dados fica confuso sobre qual coluna real queremos dizer e onde. Existem duas soluções para resolver isso. O primeiro é agradável e simples, podemos tableName.columnNamedizer ao banco de dados exatamente o que queremos dizer, assim:

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)

O outro é provavelmente usado com mais frequência e é chamado de alias de tabela. As tabelas neste exemplo têm nomes simples curtos e agradáveis, mas digitar algo como KPI_DAILY_SALES_BY_DEPARTMENTprovavelmente envelheceria rapidamente, portanto, uma maneira simples é apelidar a tabela assim:

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

Agora, de volta à solicitação. Como você pode ver, temos as informações de que precisamos, mas também temos as que não foram solicitadas, por isso precisamos incluir uma cláusula where na declaração para obter apenas os carros esportivos conforme solicitado. Como eu prefiro o método de alias da tabela em vez de usar os nomes das tabelas repetidamente, continuarei com isso a partir deste ponto.

Claramente, precisamos adicionar uma cláusula where à nossa consulta. Podemos identificar carros esportivos por ID=1ou model='Sports'. Como o ID é indexado e a chave primária (e acontece que é menos digitada), vamos usá-lo em nossa 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! O chefe está feliz. É claro que, sendo um chefe e nunca sendo feliz com o que pediu, ele olha as informações e depois diz que também quero as cores .

Ok, então já temos uma boa parte de nossa consulta escrita, mas precisamos usar uma terceira tabela, que é cores. Agora, nossa tabela de informações principal carsarmazena a identificação da cor do carro e isso é vinculado à coluna de identificação da cor. Assim, de maneira semelhante ao original, podemos entrar em uma terceira tabela:

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)

Porra, embora a tabela tenha sido unida corretamente e as colunas relacionadas tenham sido vinculadas, esquecemos de extrair as informações reais da nova tabela 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)

Certo, esse é o chefe de nossas costas por um momento. Agora, para explicar um pouco mais detalhadamente isso. Como você pode ver, a fromcláusula em nossa declaração vincula nossa tabela principal (eu geralmente uso uma tabela que contém informações em vez de uma tabela de pesquisa ou dimensão. A consulta funcionaria da mesma forma que as tabelas alternadas, mas faz menos sentido quando voltamos a esta consulta para lê-la dentro de alguns meses, por isso é melhor tentar escrever uma consulta que seja agradável e fácil de entender - faça um layout intuitivo, use um recuo agradável para que tudo fique tão claro quanto possível. Se você continuar ensinando outras pessoas, tente instilar essas características em suas consultas - especialmente se estiver solucionando problemas.

É inteiramente possível continuar vinculando cada vez mais tabelas dessa maneira.

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

Embora eu tenha esquecido de incluir uma tabela na qual possamos juntar mais de uma coluna na joindeclaração, aqui está um exemplo. Se a modelstabela tivesse modelos específicos de marca e, portanto, também tivesse uma coluna chamada brandque vinculava de volta à brandstabela no IDcampo, isso poderia ser feito da seguinte maneira:

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

Você pode ver, a consulta acima não apenas vincula as tabelas unidas à carstabela principal , mas também especifica as junções entre as tabelas já unidas. Se isso não foi feito, o resultado é chamado de junção cartesiana - que é dba speak for bad. Uma junção cartesiana é aquela em que as linhas são retornadas porque as informações não informam ao banco de dados como limitar os resultados; portanto, a consulta retorna todas as linhas que atendem aos critérios.

Portanto, para dar um exemplo de junção cartesiana, vamos executar a seguinte 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)

Bom Deus, isso é feio. No entanto, no que diz respeito ao banco de dados, é exatamente o que foi solicitado. Na consulta, solicitamos o IDfrom carse o modelfrom models. No entanto, como não especificamos como ingressar nas tabelas, o banco de dados corresponde a todas as linhas da primeira tabela com todas as linhas da segunda tabela.

Ok, então o chefe está de volta e ele quer mais informações novamente. Eu quero a mesma lista, mas também inclui 4WDs nela .

Isso, no entanto, nos dá uma ótima desculpa para examinar duas maneiras diferentes de conseguir isso. Poderíamos adicionar outra condição à 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

Embora o descrito acima funcione perfeitamente bem, vamos ver de forma diferente, essa é uma ótima desculpa para mostrar como uma unionconsulta funcionará.

Sabemos que o seguinte retornará todos os carros esportivos:

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

E o seguinte retornaria todos os 4WDs:

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

Portanto, adicionando uma union allcláusula entre eles, os resultados da segunda consulta serão anexados aos resultados da primeira 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 você pode ver, os resultados da primeira consulta são retornados primeiro, seguidos pelos resultados da segunda consulta.

Neste exemplo, é claro que teria sido muito mais fácil simplesmente usar a primeira consulta, mas as unionconsultas podem ser ótimas para casos específicos. Eles são uma ótima maneira de retornar resultados específicos de tabelas de tabelas que não são facilmente unidas - ou, por esse motivo, tabelas completamente não relacionadas. Existem algumas regras a seguir, no entanto.

  • Os tipos de coluna da primeira consulta devem corresponder aos tipos de coluna de todas as outras consultas abaixo.
  • Os nomes das colunas da primeira consulta serão usados ​​para identificar todo o conjunto de resultados.
  • O número de colunas em cada consulta deve ser o mesmo.

Agora, você pode estar se perguntando qual é a diferença entre usar unione union all. UMAunion consulta removerá duplicatas, enquanto a union allnão. Isso significa que há um pequeno impacto no desempenho quando usado em unionexcesso, union allmas os resultados podem valer a pena - não vou especular sobre esse tipo de coisa.

Nesta nota, pode ser interessante notar algumas notas adicionais aqui.

  • Se quisermos ordenar os resultados, podemos usar um, order bymas você não pode mais usar o alias. Na consulta acima, acrescentar um order by a.IDresultaria em erro - no que diz respeito aos resultados, a coluna é chamadaID vez de a.ID- mesmo que o mesmo alias tenha sido usado nas duas consultas.
  • Só podemos ter um order by declaração, e deve ser a última declaração.

Para os próximos exemplos, estou adicionando algumas linhas extras às nossas tabelas.

Eu adicionei Holdenà tabela de marcas. Eu também adicionei uma linha carsque tem o colorvalor de12 - que não tem referência na tabela de cores.

Ok, o chefe está de volta, gritando pedidos - * Quero uma contagem de cada marca que carregamos e o número de carros nela! `- Típico, chegamos a uma seção interessante de nossa discussão e o chefe quer mais trabalho .

Rightyo, então a primeira coisa que precisamos fazer é obter uma lista completa de possíveis marcas.

select
    a.brand
from
    brands a

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

Agora, quando juntamos isso à nossa tabela de carros, obtemos o seguinte 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)

O que é obviamente um problema - não estamos vendo nenhuma menção à adorável Holden marca que adicionei.

Isso ocorre porque uma junção procura por linhas correspondentes nas duas tabelas. Como não há dados nos carros desse tipo, Holdeneles não são retornados. É aqui que podemos usar uma outerassociação. Isso retornará todos os resultados de uma tabela, independentemente de serem correspondidos na outra tabela ou não:

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)

Agora que temos isso, podemos adicionar uma função agregada adorável para obter uma contagem e tirar o chefe de nossas costas por um 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)

E com isso, o chefe foge.

Agora, para explicar isso com mais detalhes, as junções externas podem ser do tipo leftou right. A esquerda ou direita define qual tabela está totalmente incluída. A left outer joinincluirá todas as linhas da tabela à esquerda, enquanto (você adivinhou) umaright outer join traz todos os resultados da tabela à direita para os resultados.

Alguns bancos de dados permitem um full outer joinque traga de volta resultados (correspondidos ou não) de ambos tabelas, mas isso não é suportado em todos os bancos de dados.

Agora, provavelmente eu acho que, neste momento, você está se perguntando se pode ou não mesclar tipos de junção em uma consulta - e a resposta é sim, você pode absolutamente.

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)

Então, por que esse não é o resultado esperado? Isso porque, embora tenhamos selecionado a junção externa de carros para marcas, ela não foi especificada na junção de cores - portanto, essa junção específica trará apenas resultados correspondentes às duas tabelas.

Aqui está a consulta que funcionaria para obter os resultados que esperávamos:

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, temos duas junções externas na consulta e os resultados estão chegando conforme o esperado.

Agora, e os outros tipos de junções que você pergunta? E as interseções?

Bem, nem todos os bancos de dados suportam o intersection mas praticamente todos os bancos de dados permitem criar uma interseção por meio de uma junção (ou uma instrução where bem estruturada, no mínimo).

Uma interseção é um tipo de junção um pouco semelhante ao uniondescrito acima - mas a diferença é que apenas retorna linhas de dados que são idênticas (e eu quero dizer idênticas) entre as várias consultas individuais unidas pela união. Somente linhas idênticas em todos os aspectos serão retornadas.

Um exemplo simples seria o seguinte:

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

Enquanto uma unionconsulta normal retornaria todas as linhas da tabela (a primeira consulta retornando qualquer coisa ID>2e a segunda qualquer coisa que tivesse ID<4) que resultaria em um conjunto completo, uma consulta de interseção retornaria apenas a correspondência de linha id=3, pois atendia aos dois critérios.

Agora, se o seu banco de dados não suportar uma intersectconsulta, o acima pode ser facilmente realizado com a seguinte 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)

Se você deseja executar uma interseção entre duas tabelas diferentes usando um banco de dados que não suporta inerentemente uma consulta de interseção, será necessário criar uma junção em todas as colunas das tabelas.

Fluffeh
fonte
2
@Fluffeh Respostas agradáveis. Eu tenho uma sugestão: se você deseja torná-lo um tutorial SQL matador, só falta adicionar diagramas de Venn; Eu entendi imediatamente esquerda e direita se junta graças a eles. Pedido pessoal: Você tem algum tutorial sobre erros comuns / redução de desempenho?
StrayChild01
25
Oh meu. Minha roda de rolagem está quebrada. Ótima pergunta e resposta. Eu gostaria de poder votar isso 10 vezes.
Amal Murali
3
Hehe, obrigado pelo feedback positivo. Continue rolando, porém, essa foi apenas a primeira resposta. SO disse a minha resposta foi muito tempo para se encaixar em uma "resposta" então eu tive que usar um pouco :)
Fluffeh
7
Honestamente, acho que essa resposta precisa ser um pouco reduzida.
einpoklum
Excelente artigo. O banco de dados se junta a 101.
maqs
101

Ok, achei este post muito interessante e gostaria de compartilhar um pouco do meu conhecimento sobre a criação de uma consulta. Obrigado por este Fluffeh . Outros que podem ler isso e sentir que estou errado têm 101% de liberdade para editar e criticar minha resposta. ( Honestamente, sinto-me muito agradecido por corrigir meus erros. )

Vou postar algumas das perguntas mais frequentes na MySQLtag.


Truque nº 1 ( linhas que correspondem a várias condições )

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');

QUESTÃO

Encontre todos os filmes que pertencem a pelo menos ambos Comedy e Romancecategorias.

Solução

Esta pergunta pode ser muito complicada às vezes. Pode parecer que uma consulta como essa seja a resposta: -

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

Demonstração do SQLFiddle

o que é definitivamente muito errado, porque não produz resultado . A explicação disso é que existe apenas um valor válido CategoryNameem cada linha . Por exemplo, a primeira condição retorna verdadeira , a segunda condição é sempre falsa. Assim, usando o ANDoperador, ambas as condições devem ser verdadeiras; caso contrário, será falso. Outra consulta é assim,

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

Demonstração do SQLFiddle

e o resultado ainda está incorreto porque corresponde ao registro que possui pelo menos uma correspondência no categoryName. A solução real seria contando o número de instâncias de registro por filme . O número de instâncias deve corresponder ao número total dos valores fornecidos na condição.

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

Demonstração do SQLFiddle (a resposta)


Truque 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');

QUESTÃO

Encontre a versão mais recente em cada software. Exibir as seguintes colunas: SoftwareName, Descriptions, LatestVersion( a partir de coluna VersionNo ),DateReleased

Solução

Alguns desenvolvedores de SQL usam incorretamente MAX()a função agregada. Eles tendem a criar assim,

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

Demonstração do SQLFiddle

(a maioria dos RDBMS gera um erro de sintaxe nisso, por não especificar algumas das colunas não agregadas na group bycláusula ) o resultado produz o correto LatestVersionem cada software, mas obviamente DateReleasedestá incorreto. MySQLnão suporta Window Functionse Common Table Expressionainda como alguns RDBMS já fazem. A solução alternativa para esse problema é criar um subqueryque obtenha o máximo de indivíduo versionNoem cada software e, posteriormente, seja associado às outras tabelas.

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

Demonstração do SQLFiddle (a resposta)


Então foi isso. Estarei postando outra assim que me lembrar de qualquer outra FAQ na MySQLtag. Obrigado por ler este pequeno artigo. Espero que você tenha pelo menos um pouco de conhecimento disso.

ATUALIZAÇÃO 1


Truque n ° 3 (localizando o registro mais recente entre dois IDs )

Esquema determinado

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');

QUESTÃO

Encontre a última conversa entre dois usuários.

Solução

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
)

Demonstração do SQLFiddle

John Woo
fonte
Impressionante! Uma ressalva, John, sua primeira solução funciona apenas porque há uma restrição exclusiva nos dois campos. Você poderia ter usado uma solução mais geral para ajudar com um problema comum. Na minha opinião, a única solução é fazer seleções individuais para comedye romance. Havingnão
serve
@nawfal não realmente, se restrição exclusiva não foi adicionado, então você precisa adicionar distinctna cláusula having SQLFiddle Demonstração : D
John Woo
63

Parte 2 - Subconsultas

Ok, agora o chefe voltou a invadir - quero uma lista de todos os nossos carros com a marca e um total de quantos dessa marca temos!

Esta é uma ótima oportunidade para usar o próximo truque em nosso pacote de itens SQL - a subconsulta. Se você não estiver familiarizado com o termo, uma subconsulta é uma consulta executada dentro de outra consulta. Existem muitas maneiras diferentes de usá-los.

Para nosso pedido, vamos primeiro montar uma consulta simples que listará cada carro e a marca:

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

Agora, se quiséssemos simplesmente obter uma contagem de carros classificados por marca, é claro que poderíamos escrever o seguinte:

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 |
+--------+-----------+

Portanto, poderíamos simplesmente adicionar a função count à nossa consulta original, certo?

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)

Infelizmente, não, não podemos fazer isso. O motivo é que, quando adicionamos o ID do carro (coluna a.ID), precisamos adicioná-lo ao grupo - então agora, quando a função de contagem funciona, existe apenas um ID correspondente por ID.

É aqui que, no entanto, podemos usar uma subconsulta - na verdade, podemos fazer dois tipos de subconsulta completamente diferentes que retornarão os mesmos resultados que precisamos para isso. O primeiro é simplesmente colocar a subconsulta na selectcláusula. Isso significa que cada vez que obtemos uma linha de dados, a subconsulta é executada, obtém uma coluna de dados e a coloca na nossa linha de dados.

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)

E Bam !, isso nos faria. Se você notou, essa subconsulta terá que ser executada para cada linha de dados que retornamos. Mesmo neste pequeno exemplo, temos apenas cinco marcas diferentes de carro, mas a subconsulta foi realizada onze vezes, pois temos onze linhas de dados que estamos retornando. Portanto, nesse caso, não parece a maneira mais eficiente de escrever código.

Para uma abordagem diferente, vamos executar uma subconsulta e fingir que é uma tabela:

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)

Ok, temos os mesmos resultados (ordenados um pouco diferentes - parece que o banco de dados queria retornar resultados ordenados pela primeira coluna que escolhemos desta vez) - mas os mesmos números corretos.

Então, qual é a diferença entre os dois - e quando devemos usar cada tipo de subconsulta? Primeiro, vamos entender como essa segunda consulta funciona. Selecionamos duas tabelas na fromcláusula de nossa consulta e, em seguida, escrevemos uma consulta e informamos ao banco de dados que era de fato uma tabela - com a qual o banco de dados está perfeitamente satisfeito. Não pode haver alguns benefícios a usar este método (bem como algumas limitações). O principal é que essa subconsulta foi executada uma vez . Se nosso banco de dados contivesse um grande volume de dados, poderia haver uma grande melhoria em relação ao primeiro método. No entanto, como estamos usando isso como uma tabela, precisamos trazer linhas extras de dados - para que eles possam realmente se unir de volta às nossas linhas de dados. Nós também temos que ter certeza de que há o suficientelinhas de dados se vamos usar uma junção simples, como na consulta acima. Se você se lembrar, a junção puxará apenas as linhas que possuem dados correspondentes nos dois lados da junção. Se não tomarmos cuidado, isso poderá resultar no retorno de dados válidos da tabela de carros, se não houver uma linha correspondente nessa subconsulta.

Agora, olhando para a primeira subconsulta, também existem algumas limitações. porque estamos puxando para trás os dados em uma única linha, podemos apenas puxar para trás uma linha de dados. Subqueries utilizados na selectcláusula de uma consulta muito frequentemente utilizar apenas uma função de agregação, tais como sum, count, maxou outra função semelhante agregado. Eles não precisam , mas é assim que costumam ser escritos.

Portanto, antes de prosseguirmos, vamos dar uma rápida olhada em onde mais podemos usar uma subconsulta. Podemos usá-lo na wherecláusula - agora, este exemplo é um pouco artificial, pois em nosso banco de dados, existem maneiras melhores de obter os seguintes dados, mas, como é apenas um exemplo, vamos dar uma olhada:

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

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

Isso nos retorna uma lista de IDs e nomes de marcas (a segunda coluna é adicionada apenas para nos mostrar as marcas) que contém a letra ono nome.

Agora, poderíamos usar os resultados dessa consulta na 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 você pode ver, mesmo que a subconsulta retornasse os três IDs de marca, nossa tabela de carros só tinha entradas para dois deles.

Nesse caso, para obter mais detalhes, a subconsulta está funcionando como se tivéssemos escrito o seguinte 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)

Novamente, você pode ver como uma subconsulta versus entradas manuais alterou a ordem das linhas ao retornar do banco de dados.

Enquanto estamos discutindo subconsultas, vamos ver o que mais podemos fazer com uma subconsulta:

  • Você pode colocar uma subconsulta dentro de outra subconsulta, e assim por diante. Existe um limite que depende do seu banco de dados, mas, com a falta de funções recursivas de algum programador insano e maníaco, a maioria das pessoas nunca alcançará esse limite.
  • Você pode colocar várias subconsultas em uma única consulta, algumas na selectcláusula, algumas na fromcláusula e mais algumas na wherecláusula - lembre-se de que cada uma inserida está tornando sua consulta mais complexa e provavelmente demorará mais tempo para ser executada. executar.

Se você precisar escrever algum código eficiente, pode ser benéfico escrever a consulta de várias maneiras e ver (cronometrando-a ou usando um plano de explicação) qual é a consulta ideal para obter seus resultados. A primeira maneira que funciona nem sempre pode ser a melhor maneira de fazê-lo.

Fluffeh
fonte
Muito importante para novos desenvolvedores: as subconsultas provavelmente são executadas uma vez para cada resultado, a menos que você possa usar a subconsulta como uma junção (mostrada acima).
Xeoncross 15/01/19
59

Parte 3 - Truques e código eficiente

MySQL em eficiência ()

Eu pensei em adicionar alguns bits extras, para dicas e truques que surgirem.

Uma pergunta que vejo é boa: como faço para obter linhas não correspondentes de duas tabelas e vejo a resposta mais aceita como algo como o seguinte (com base na tabela de carros e marcas - que Holden listou como marca, mas não aparece na tabela de carros):

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

E sim , vai funcionar.

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

No entanto, não é eficiente em alguns bancos de dados. Aqui está um link para uma pergunta de estouro de pilha perguntando sobre isso, e aqui está um excelente artigo aprofundado, se você quiser entrar no âmago da questão.

A resposta curta é: se o otimizador não lidar com isso com eficiência, pode ser muito melhor usar uma consulta como a seguinte para obter linhas não correspondentes:

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)

Atualizar tabela com a mesma tabela na subconsulta

Ahhh, outro oldie, mas goodie - o antigo Você não pode especificar 'marcas' da tabela de destino para atualização na cláusula FROM .

O MySQL não permitirá que você execute uma update...consulta com uma subseleção na mesma tabela. Agora, você pode estar pensando: por que não colocar a cláusula where certa? Mas e se você quiser atualizar apenas a linha com a max()data entre várias outras linhas? Você não pode fazer exatamente isso em uma 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

Então, não podemos fazer isso, eh? Bem, não exatamente. Existe uma solução sorrateira que um número surpreendentemente grande de usuários não conhece - embora isso inclua alguns truques nos quais você precisará prestar atenção.

Você pode colar a subconsulta em outra subconsulta, o que coloca um espaço suficiente entre as duas consultas para que funcione. No entanto, observe que pode ser mais seguro manter a consulta em uma transação - isso impedirá que outras alterações sejam feitas nas tabelas enquanto a consulta estiver em execução.

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
fonte
3
Quero apenas observar que a construção WHERE NOT EXISTS () é praticamente idêntica do ponto de vista da eficiência, mas, na minha opinião, é muito mais fácil de ler / entender. Por outro lado, meu conhecimento é limitado ao MSSQL e não posso prometer se o mesmo acontece em outras plataformas.
deroby
Eu apenas tentei esse tipo de comparação no outro dia, onde o NOT IN () tinha uma lista com várias centenas de IDs e não havia diferença entre ele e a versão de junção da consulta. Talvez faça diferença quando você chegar aos milhares ou bilhões.
Buttle Butkus 27/09/12
18

Você pode usar o conceito de várias consultas na palavra-chave FROM. Deixe-me mostrar um exemplo:

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

Você pode usar quantas tabelas quiser. Use junções externas e união sempre que necessário, mesmo dentro de subconsultas da tabela.

Esse é um método muito fácil de envolver até tabelas e campos.

prashant1988
fonte
8

Espero que isso ache as tabelas enquanto você lê a coisa:

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
fonte