Parte 1 - Associações e Sindicatos
Esta resposta abrange:
- Parte 1
- 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 model
coluna da cars
tabela estão relacionados à models
coluna da cars
tabela que temos. Agora, sabemos que a tabela de modelos tem um ID 1
para, Sports
entã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.columnName
dizer 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_DEPARTMENT
provavelmente 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=1
ou 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 cars
armazena 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 from
clá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 join
declaração, aqui está um exemplo. Se a models
tabela tivesse modelos específicos de marca e, portanto, também tivesse uma coluna chamada brand
que vinculava de volta à brands
tabela no ID
campo, 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 à cars
tabela 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 ID
from cars
e o model
from 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 union
consulta 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 all
clá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 union
consultas 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 union
e union all
. UMAunion
consulta removerá duplicatas, enquanto a union all
não. Isso significa que há um pequeno impacto no desempenho quando usado em union
excesso, union all
mas 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 by
mas você não pode mais usar o alias. Na consulta acima, acrescentar um order by a.ID
resultaria 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 cars
que tem o color
valor 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, Holden
eles não são retornados. É aqui que podemos usar uma outer
associaçã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 left
ou right
. A esquerda ou direita define qual tabela está totalmente incluída. A left outer join
incluirá 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 join
que 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 union
descrito 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 union
consulta normal retornaria todas as linhas da tabela (a primeira consulta retornando qualquer coisa ID>2
e 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 intersect
consulta, 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.
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
MySQL
tag.Truque nº 1 ( linhas que correspondem a várias condições )
Dado este esquema
QUESTÃO
Encontre todos os filmes que pertencem a pelo menos ambos
Comedy
eRomance
categorias.Solução
Esta pergunta pode ser muito complicada às vezes. Pode parecer que uma consulta como essa seja a resposta: -
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
CategoryName
em cada linha . Por exemplo, a primeira condição retorna verdadeira , a segunda condição é sempre falsa. Assim, usando oAND
operador, ambas as condições devem ser verdadeiras; caso contrário, será falso. Outra consulta é assim,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.Demonstração do SQLFiddle (a resposta)
Truque No. 2 ( registro máximo para cada entrada )
Esquema dado,
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,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 by
cláusula ) o resultado produz o corretoLatestVersion
em cada software, mas obviamenteDateReleased
está incorreto.MySQL
não suportaWindow Functions
eCommon Table Expression
ainda como alguns RDBMS já fazem. A solução alternativa para esse problema é criar umsubquery
que obtenha o máximo de indivíduoversionNo
em cada software e, posteriormente, seja associado às outras tabelas.Demonstração do SQLFiddle (a resposta)
Então foi isso. Estarei postando outra assim que me lembrar de qualquer outra FAQ na
MySQL
tag. 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
QUESTÃO
Encontre a última conversa entre dois usuários.
Solução
Demonstração do SQLFiddle
fonte
comedy
eromance
.Having
nãodistinct
na cláusula having SQLFiddle Demonstração : DParte 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:
Agora, se quiséssemos simplesmente obter uma contagem de carros classificados por marca, é claro que poderíamos escrever o seguinte:
Portanto, poderíamos simplesmente adicionar a função count à nossa consulta original, certo?
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
select
clá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.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:
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
from
clá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
select
cláusula de uma consulta muito frequentemente utilizar apenas uma função de agregação, tais comosum
,count
,max
ou 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
where
clá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: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
o
no nome.Agora, poderíamos usar os resultados dessa consulta na cláusula where:
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:
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:
select
cláusula, algumas nafrom
cláusula e mais algumas nawhere
clá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.
fonte
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):
E sim , vai funcionar.
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:
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 amax()
data entre várias outras linhas? Você não pode fazer exatamente isso em uma cláusula where.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.
fonte
Você pode usar o conceito de várias consultas na palavra-chave FROM. Deixe-me mostrar um exemplo:
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.
fonte
Espero que isso ache as tabelas enquanto você lê a coisa:
jsfiddle
fonte