SQL-запрос возвращает данные из нескольких таблиц


434

Я хотел бы знать следующее:

  • Как получить данные из нескольких таблиц в моей базе данных?
  • какие методы существуют для этого?
  • Что такое объединения и союзы и чем они отличаются друг от друга?
  • Когда я должен использовать каждый из них по сравнению с другими?

Я планирую использовать это в моем (например, PHP) приложении, но не хочу выполнять несколько запросов к базе данных, какие варианты у меня есть, чтобы получить данные из нескольких таблиц в одном запросе?

Примечание: я пишу это, поскольку я хотел бы иметь возможность дать ссылку на хорошо написанное руководство по многочисленным вопросам, с которыми я постоянно сталкиваюсь в очереди PHP, поэтому я могу сослаться на это для более подробной информации, когда я публикую ответ.

Ответы покрывают следующее:

  1. Часть 1 - Объединения и Союзы
  2. Часть 2 - Подзапросы
  3. Часть 3 - хитрости и эффективный код
  4. Часть 4 - Подзапросы в предложении From
  5. Часть 5 - Смешанная сумка с хитростями Джона

Ответы:


469

Часть 1 - Объединения и Союзы

Этот ответ охватывает:

  1. Часть 1
  2. Часть 2
    • Подзапросы - что это такое, где их можно использовать и на что обратить внимание
    • Декартов присоединяется к АКА - О, несчастье!

Существует несколько способов получения данных из нескольких таблиц в базе данных. В этом ответе я буду использовать синтаксис соединения ANSI-92. Это может отличаться от ряда других учебных пособий, в которых используется более старый синтаксис ANSI-89 (и если вы привыкли к 89, это может показаться гораздо менее интуитивным - но все, что я могу сказать, это попробовать), поскольку это намного проще чтобы понять, когда запросы начинают усложняться. Зачем это использовать? Есть ли прирост производительности? Не Короткий ответ нет, но это легче читать , как только вы привыкнете к нему. С этим синтаксисом легче читать запросы, написанные другими людьми.

Я также собираюсь использовать концепцию маленького каряда, у которого есть база данных, чтобы отслеживать, какие автомобили у него есть. Владелец нанял вас в качестве своего специалиста по ИТ-компьютерам и ожидает, что вы сможете сбросить ему данные, которые он запрашивает, без промедления.

Я сделал несколько таблиц поиска, которые будут использоваться в финальной таблице. Это даст нам разумную модель для работы. Для начала я буду выполнять запросы к образцу базы данных, которая имеет следующую структуру. Я постараюсь подумать об общих ошибках, которые допускаются при запуске, и объясню, что с ними не так, а также, конечно, покажу, как их исправить.

Первая таблица - это просто список цветов, чтобы мы знали, какие цвета у нас на автомобильном дворе.

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)

Таблица брендов определяет различные марки автомобилей, которые может продать Caryard.

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)

Таблица моделей будет охватывать различные типы автомобилей, для этого будет проще использовать различные типы автомобилей, а не фактические модели автомобилей.

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)

И, наконец, связать все эти другие столы, стол, который связывает все воедино. Поле идентификатора фактически является уникальным номером лота, используемым для идентификации автомобилей.

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)

Это даст нам достаточно данных (я надеюсь), чтобы охватить приведенные ниже примеры различных типов объединений, а также даст достаточно данных, чтобы сделать их полезными.

Так что, понимая, в чем дело, босс хочет знать идентификационные данные всех спортивных автомобилей, которые у него есть .

Это простое соединение за двумя столами. У нас есть таблица, которая идентифицирует модель и таблицу с имеющимся запасом в ней. Как видите, данные в modelстолбце carsтаблицы относятся к modelsстолбцу carsтаблицы, которую мы имеем. Теперь мы знаем, что таблица моделей имеет идентификатор 1for, Sportsпоэтому давайте напишем соединение.

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

Так что этот запрос выглядит хорошо, верно? Мы определили две таблицы и содержат необходимую нам информацию, а также используем соединение, которое правильно определяет столбцы для объединения.

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

О нет! Ошибка в нашем первом запросе! Да и это слива. Видите ли, у запроса действительно есть правильные столбцы, но некоторые из них существуют в обеих таблицах, поэтому база данных запутывается в том, какой фактический столбец мы имеем в виду и где. Есть два решения, чтобы решить эту проблему. Первый - простой и понятный, мы можем использовать tableName.columnNameбазу данных, чтобы точно сказать, что мы имеем в виду, например:

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)

Другой, вероятно, чаще используется и называется псевдонимами таблиц. Таблицы в этом примере имеют приятные и короткие простые имена, но, набрав что-то вроде, KPI_DAILY_SALES_BY_DEPARTMENTможно быстро устареть, поэтому простой способ - назвать таблицу так:

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

Теперь вернемся к запросу. Как вы видите, у нас есть информация, которая нам нужна, но у нас также есть информация, которая не запрашивалась, поэтому нам нужно включить в заявление пункт where, чтобы получить спортивные автомобили только в соответствии с запросом. Поскольку я предпочитаю метод псевдонимов таблиц, а не использовать имена таблиц снова и снова, я буду придерживаться его с этого момента.

Ясно, что нам нужно добавить предложение where в наш запрос. Мы можем определить спортивные автомобили либо по, ID=1либо model='Sports'. Так как идентификатор индексируется и является первичным ключом (и, как оказалось, он меньше набирает), давайте использовать это в нашем запросе.

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)

Бинго! Босс счастлив. Конечно, будучи начальником и никогда не радуясь тому, о чем он просил, он смотрит на информацию и говорит, что я тоже хочу цвета .

Итак, у нас уже есть хорошая часть нашего запроса, но нам нужно использовать третью таблицу, которая является цветами. Теперь в нашей главной информационной таблице carsхранится идентификатор цвета автомобиля, и он ссылается на столбец идентификатора цвета. Таким образом, аналогично оригиналу, мы можем присоединиться к третьей таблице:

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)

Черт, хотя таблица была правильно соединена и связанные столбцы были связаны, мы забыли извлечь фактическую информацию из новой таблицы, которую мы только что связали.

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)

Да, это босс с нашей спины на мгновение. Теперь, чтобы объяснить это немного подробнее. Как вы можете видеть, fromпредложение в нашем операторе связывает нашу основную таблицу (я часто использую таблицу, которая содержит информацию, а не таблицу поиска или измерения). Запрос будет работать так же хорошо, как и все таблицы, но не имеет смысла, когда мы возвращаемся к этому запросу, чтобы прочитать его через несколько месяцев, поэтому часто лучше всего попытаться написать запрос, который будет приятным и легким для понимания - выложите его интуитивно, используйте хороший отступ, чтобы все было так ясно, как это может быть. Если вы продолжаете обучать других, попытайтесь привить эти характеристики в их запросах - особенно, если вы будете их устранять.

Таким образом, можно продолжать связывать все новые и новые таблицы.

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

Хотя я забыл включить таблицу, в которую мы можем захотеть объединить более одного столбца в joinвыражении, вот пример. Если modelsтаблица имела модели для конкретного бренда и, следовательно, также имела столбец, brandкоторый связывался с brandsтаблицей на IDполе, это можно сделать следующим образом:

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

Как видите, приведенный выше запрос не только связывает объединенные таблицы с основной carsтаблицей, но также определяет объединения между уже объединенными таблицами. Если это не было сделано, результат называется декартовым соединением - что означает dba плохо. Декартово соединение - это то, где строки возвращаются, потому что информация не говорит базе данных, как ограничить результаты, поэтому запрос возвращает все строки, которые соответствуют критериям.

Итак, чтобы привести пример декартового объединения, давайте запустим следующий запрос:

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)

Боже мой, это безобразно. Однако, что касается базы данных, это именно то , о чем просили. В запросе мы запросили IDот carsи modelот models. Однако, поскольку мы не указали, как объединять таблицы, база данных сопоставляет каждую строку из первой таблицы с каждой строкой из второй таблицы.

Итак, босс вернулся, и ему снова нужна дополнительная информация. Я хочу тот же список, но также включить в него 4WD .

Это, однако, дает нам отличный повод взглянуть на два разных способа сделать это. Мы можем добавить еще одно условие к предложению where, например:

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

Хотя вышеперечисленное будет работать отлично, давайте посмотрим на это по-другому, но это отличный повод показать, как unionбудет работать запрос.

Мы знаем, что следующее вернет все спортивные автомобили:

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

И следующее вернет все 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

Таким образом, добавив 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=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)

Как видите, результаты первого запроса возвращаются первыми, а затем результаты второго запроса.

В этом примере, конечно, было бы намного проще просто использовать первый запрос, но unionзапросы могут быть полезны для конкретных случаев. Они являются отличным способом вернуть конкретные результаты из таблиц из таблиц, которые нелегко объединить - или, в этом отношении, совершенно не связанные таблицы. Однако есть несколько правил, которым нужно следовать.

  • Типы столбцов из первого запроса должны совпадать с типами столбцов из любого другого запроса ниже.
  • Имена столбцов из первого запроса будут использоваться для идентификации всего набора результатов.
  • Количество столбцов в каждом запросе должно быть одинаковым.

Теперь вам может быть интересно, в чем разница между использованием unionи union all. unionЗапрос будет удалять дубликаты, а union allне будет. Это означает, что при использовании unionover есть небольшой скачок производительности, union allно результаты могут стоить того - хотя я не буду спекулировать на подобных вещах.

На этой заметке, возможно, стоит отметить некоторые дополнительные заметки здесь.

  • Если мы хотим упорядочить результаты, мы можем использовать, order byно вы больше не можете использовать псевдоним. В приведенном выше запросе добавление an order by a.IDприведет к ошибке - что касается результатов, то столбец вызывается, IDа не a.ID- даже если в обоих запросах использовался один и тот же псевдоним.
  • У нас может быть только одно order byутверждение, и оно должно быть последним.

Для следующих примеров я добавляю несколько дополнительных строк в наши таблицы.

Я добавил Holdenв таблицу брендов. Я также добавил строку carsсо colorзначением, 12которое не имеет ссылки в таблице цветов.

Ладно, босс снова вернулся, выкрикивая запросы - * Я хочу подсчитать каждую марку, которую мы несем, и количество автомобилей в ней! `- Как правило, мы просто попадаем в интересный раздел нашего обсуждения, и босс хочет больше работы ,

Rightyo, поэтому первое, что нам нужно сделать, это получить полный список возможных брендов.

select
    a.brand
from
    brands a

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

Теперь, когда мы присоединяем это к нашей таблице машин, мы получаем следующий результат:

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)

Что, конечно, является проблемой - мы не видим упоминаний о прекрасном Holdenбренде, который я добавил.

Это потому, что объединение ищет совпадающие строки в обеих таблицах. Поскольку в автомобилях нет данных такого типа, Holdenони не возвращаются. Здесь мы можем использовать outerсоединение. Это вернет все результаты из одной таблицы независимо от того, совпадают ли они в другой таблице:

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)

Теперь, когда у нас это есть, мы можем добавить прекрасную функцию агрегирования, чтобы получить счет и на мгновение снять босса с наших плеч.

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)

И с этим, далеко босс скрывается.

Теперь, чтобы объяснить это более подробно, внешние соединения могут иметь тип leftили right. Влево или вправо определяет, какая таблица полностью включена. A left outer joinбудет включать в себя все строки из таблицы слева, а (как вы уже догадались) a right outer joinприносит все результаты из таблицы справа в результаты.

В некоторых базах данных допускается full outer joinвозврат результатов (независимо от того, совпадают они или нет) из обеих таблиц, но это поддерживается не во всех базах данных.

Теперь я, вероятно, думаю, что в данный момент вы задаетесь вопросом, можете ли вы объединить типы соединений в запросе - и ответ «да», вы, безусловно, можете.

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)

Итак, почему это не те результаты, которые ожидались? Это потому, что, хотя мы выбрали внешнее объединение из автомобилей по брендам, оно не было указано в соединении с цветами - так что конкретное объединение будет возвращать только результаты, которые соответствуют в обеих таблицах.

Вот запрос, который будет работать, чтобы получить ожидаемые результаты:

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)

Как мы видим, у нас есть два внешних соединения в запросе, и результаты достигаются, как и ожидалось.

А как насчет других типов соединений, которые вы спрашиваете? Как насчет перекрестков?

Ну, не все базы данных поддерживают, intersectionно почти все базы данных позволят вам создать пересечение посредством соединения (или, по крайней мере, хорошо структурированного оператора where).

Пересечение - это тип объединения, несколько похожий на unionописанный выше, но отличие состоит в том, что он возвращает только те строки данных, которые идентичны (и я имею в виду идентичные) между различными отдельными запросами, объединенными объединением. Будут возвращены только те строки, которые идентичны во всех отношениях.

Простой пример будет таким:

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

В то время как обычный unionзапрос вернул бы все строки таблицы (первый запрос возвратил что-либо поверх, ID>2а второй что-либо имел ID<4), что привело бы к полному набору, запрос на пересечение вернул бы только совпадение строк, id=3поскольку оно удовлетворяет обоим критериям.

Теперь, если ваша база данных не поддерживает intersectзапрос, вышеприведенное можно легко выполнить с помощью следующего запроса:

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)

Если вы хотите выполнить пересечение двух разных таблиц, используя базу данных, которая по своей сути не поддерживает запрос пересечения, вам нужно будет создать соединение для каждого столбца таблиц.


2
@Fluffeh Хорошие ответы. У меня есть предложение: если вы хотите сделать его учебником по SQL, вам не хватает только добавления диаграмм Венна; Я сразу понял, что левое и правое соединения благодаря им. Персональный запрос: есть ли у вас учебник по распространенным ошибкам / настройке производительности?
StrayChild01

25
Боже мой Мое колесо прокрутки сломано. Отличный вопрос и ответ. Хотел бы я поднять это 10 раз.
Амаль Мурали

3
Хе-хе, спасибо за положительный отзыв. Продолжайте прокручивать, хотя, это был только первый ответ. ТАК сказал, что мой ответ был слишком длинным, чтобы вписать его в один «ответ», поэтому мне пришлось использовать несколько :)
Fluffeh

7
Честно говоря, я думаю, что этот ответ должен быть несколько сокращен.
einpoklum

Отличная статья. База данных объединяется 101.
18:18

101

Хорошо, я нашел этот пост очень интересным, и я хотел бы поделиться некоторыми своими знаниями по созданию запроса. Спасибо за этот Fluffeh . Другие, кто может прочитать это и могут почувствовать, что я неправ, на 101% свободны редактировать и критиковать мой ответ. ( Честно говоря, я очень благодарен за исправление моей ошибки. )

Я буду публиковать некоторые часто задаваемые вопросы в MySQLтеге.


Трюк № 1 ( строки, соответствующие нескольким условиям )

Учитывая эту схему

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

ВОПРОС

Найти все фильмы , которые принадлежат , по крайней мере , как Comedy и Romanceкатегории.

Решение

Этот вопрос иногда может быть очень сложным. Может показаться, что такой запрос будет ответом:

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

SQLFiddle Demo

что, безусловно, очень неправильно, потому что это не дает результата . Объяснение в том , что есть только один действительное значение CategoryNameна каждой строке . Например, первое условие возвращает true , второе условие всегда false. Таким образом, с помощью ANDоператора оба условия должны выполняться; в противном случае это будет ложным. Другой запрос такой,

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

SQLFiddle Demo

и результат все еще неверен, потому что он соответствует записи, в которой есть хотя бы одно совпадение categoryName. Реальное решение было бы путем подсчета количества записей экземпляров на фильм . Номер экземпляра должен соответствовать общему количеству значений, указанных в условии.

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 (ответ)


Трюк № 2 ( максимальная запись для каждой записи )

Данная схема,

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

ВОПРОС

Найдите последнюю версию для каждого программного обеспечения. Показать следующие столбцы: SoftwareName, Descriptions, LatestVersion( из колонки VersionNo ),DateReleased

Решение

Некоторые разработчики SQL по ошибке используют MAX()агрегатную функцию. Они имеют тенденцию создавать, как это,

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

SQLFiddle Demo

( большинство СУБД генерирует синтаксическую ошибку в этом случае из-за того, что не указаны некоторые неагрегированные столбцы в group byпредложении ), результат выдает корректный результат LatestVersionдля каждого программного обеспечения, но, очевидно DateReleased, неверный. MySQLне поддерживает Window Functionsи Common Table Expressionеще некоторые СУБД уже делают. Обходной путь для этой проблемы заключается в создании переменной, subqueryкоторая получает индивидуальный максимум versionNoдля каждого программного обеспечения, а затем объединяется с другими таблицами.

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 (ответ)


Так вот и все. Я буду публиковать еще один, как только я вспоминаю другие часто задаваемые вопросы по MySQLтегу. Спасибо за чтение этой маленькой статьи. Я надеюсь, что вы хотя бы получили хоть немного знаний об этом.

ОБНОВЛЕНИЕ 1


Трюк № 3 ( Поиск последней записи между двумя идентификаторами )

Данная схема

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

ВОПРОС

Найти последний разговор между двумя пользователями.

Решение

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
)

SQLFiddle Demo


Потрясающие! Предостережение Джон, ваше первое решение работает только потому, что существует уникальное ограничение на два поля. Вы могли бы использовать более общее решение, чтобы помочь с общей проблемой. На мой взгляд, единственное решение - сделать индивидуальный выбор для comedyи romance. Havingтогда не подходит ..
nawfal

@nawfal не совсем, если уникальное ограничение не было добавлено, тогда вам нужно добавить distinctв предложение имение SQLFiddle Demo : D
Джон Ву,

63

Часть 2 - Подзапросы

Хорошо, теперь босс снова ворвался - я хочу список всех наших автомобилей с маркой и общее количество этой марки у нас!

Это прекрасная возможность использовать следующий трюк в нашем наборе SQL вкусностей - подзапрос. Если вы не знакомы с этим термином, подзапрос - это запрос, который выполняется внутри другого запроса. Есть много разных способов их использования.

Для нашего запроса, давайте сначала соберем простой запрос, в котором будут перечислены все автомобили и марки:

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

Теперь, если мы хотим просто подсчитать количество автомобилей, отсортированных по марке, мы можем, конечно, написать это:

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

Итак, мы должны иметь возможность просто добавить функцию count к нашему исходному запросу, верно?

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)

К сожалению, нет, мы не можем этого сделать. Причина в том, что когда мы добавляем идентификатор автомобиля (столбец a.ID), мы должны добавить его в группу, так что теперь, когда работает функция подсчета, для каждого идентификатора соответствует только один идентификатор.

Однако здесь мы можем использовать подзапрос - фактически мы можем сделать два совершенно разных типа подзапроса, которые будут возвращать те же результаты, которые нам нужны для этого. Первый - просто поместить подзапрос в selectпредложение. Это означает, что каждый раз, когда мы получаем строку данных, подзапрос будет выполняться, получать столбец данных и затем вставлять его в наш ряд данных.

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)

И Бэм !, это сделало бы нас. Однако, если вы заметили, этот подзапрос должен будет выполняться для каждой строки данных, которую мы возвращаем. Даже в этом небольшом примере у нас всего пять разных марок автомобилей, но подзапрос выполнялся одиннадцать раз, поскольку у нас есть одиннадцать строк данных, которые мы возвращаем. Так что в данном случае это не самый эффективный способ написания кода.

Для другого подхода давайте запустим подзапрос и представим, что это таблица:

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)

Итак, у нас есть одинаковые результаты (упорядоченные немного по-другому - кажется, что база данных хотела вернуть результаты, упорядоченные по первому столбцу, который мы выбрали на этот раз) - но те же правильные числа.

Итак, в чем разница между ними - и когда мы должны использовать каждый тип подзапроса? Во-первых, давайте удостоверимся, что мы понимаем, как работает этот второй запрос. Мы выбрали две таблицы в fromпредложении нашего запроса, а затем написали запрос и сказали базе данных, что на самом деле это была таблица, - что база данных вполне устраивает. Там могут быть некоторые преимущества использования этого метода (а также некоторые ограничения). Прежде всего, этот подзапрос выполнялся один раз . Если бы наша база данных содержала большой объем данных, вполне может быть значительное улучшение по сравнению с первым методом. Однако, поскольку мы используем это как таблицу, мы должны добавить дополнительные строки данных, чтобы они могли быть присоединены к нашим строкам данных. Мы также должны быть уверены, что есть достаточностроки данных, если мы собираемся использовать простое соединение, как в запросе выше. Если вы помните, объединение будет вытягивать только те строки, которые имеют совпадающие данные с обеих сторон объединения. Если мы не будем осторожны, это может привести к тому, что действительные данные не будут возвращены из нашей таблицы автомобилей, если в этом подзапросе не было подходящей строки.

Теперь, оглядываясь на первый подзапрос, также есть некоторые ограничения. Поскольку мы собираем данные обратно в одну строку, мы можем ТОЛЬКО вернуть одну строку данных. Подзапросы , используемые в selectпункте запроса очень часто используют только агрегатную функцию , такие как sum, count, maxили другую аналогичную совокупную функцию. Им не нужно , но так часто пишут.

Итак, прежде чем мы продолжим, давайте кратко рассмотрим, где еще мы можем использовать подзапрос. Мы можем использовать его в whereпредложении - теперь этот пример немного надуманный, поскольку в нашей базе данных есть лучшие способы получения следующих данных, но если посмотреть только на пример, давайте посмотрим:

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

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

Это возвращает нам список идентификаторов брендов и названий брендов (второй столбец добавлен только для того, чтобы показать нам бренды), которые содержат букву oв названии.

Теперь мы можем использовать результаты этого запроса в предложении 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)

Как вы можете видеть, несмотря на то, что подзапрос возвращал три идентификатора бренда, наша таблица автомобилей содержала записи только для двух из них.

В этом случае, для более подробной информации, подзапрос работает так, как если бы мы написали следующий код:

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)

Опять же, вы можете увидеть, как подзапрос по сравнению с ручным вводом изменил порядок строк при возврате из базы данных.

Пока мы обсуждаем подзапросы, давайте посмотрим, что еще мы можем сделать с подзапросом:

  • Вы можете поместить подзапрос в другой подзапрос, и так далее, и так далее. Существует предел, который зависит от вашей базы данных, но, за исключением рекурсивных функций некоторых безумных и маниакальных программистов, большинство людей никогда не достигнет этого предела.
  • Вы можете поместить несколько подзапросов в один запрос, несколько в selectпредложении, некоторые в fromпредложении и еще пару в whereпредложении - просто помните, что каждый введенный вами запрос усложняет ваш запрос и может занять больше времени. выполнить.

Если вам нужно написать какой-то эффективный код, может быть полезно написать запрос несколькими способами и посмотреть (либо по времени, либо с помощью плана объяснения), какой запрос является оптимальным для получения ваших результатов. Первый способ, который работает, не всегда может быть лучшим способом сделать это.


Очень важно для новых разработчиков: подзапросы, вероятно, запускаются один раз для каждого результата, если вы не можете использовать подзапрос как объединение (показано выше).
Xeoncross

59

Часть 3 - хитрости и эффективный код

MySQL в () эффективность

Я думал, что добавлю несколько дополнительных битов, для подсказок и трюков, которые придумали.

Один вопрос, который я вижу, довольно справедливый: как мне получить несоответствующие строки из двух таблиц, и я вижу ответ, наиболее часто принимаемый как что-то вроде следующего (основанное на нашей таблице автомобилей и марок - в которой Холден указан как бренд, но не отображается в таблице автомобилей):

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

И да, это будет работать.

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

Однако это не эффективно в некоторых базах данных. Вот ссылка на вопрос о переполнении стека, задающий этот вопрос , и вот отличная углубленная статья, если вы хотите разобраться в мелочах.

Краткий ответ: если оптимизатор не справляется с этим эффективно, может быть гораздо лучше использовать запрос, подобный следующему, чтобы получить несопоставленные строки:

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)

Обновить таблицу с той же таблицей в подзапросе

Аааа, еще одна старая, но хорошая вещь - старая. Вы не можете указать целевые таблицы «бренды» для обновления в предложении FROM .

MySQL не позволит вам выполнить update...запрос с вложенным выбором для той же таблицы. Теперь вы можете подумать, почему бы просто не вставить это в пункт where, верно? Но что, если вы хотите обновить только строку с max()датой среди множества других строк? Вы не можете точно сделать это в предложении 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

Итак, мы не можем этого сделать, а? Ну, не совсем так. Существует хитрый обходной путь, о котором удивительно большое количество пользователей не знает, хотя он включает в себя некоторые хакерские действия, на которые вам нужно будет обратить внимание.

Вы можете вставить подзапрос в другой подзапрос, который создает достаточный промежуток между двумя запросами, чтобы он работал. Однако обратите внимание, что может быть безопаснее прикрепить запрос в транзакции - это предотвратит любые другие изменения в таблицах во время выполнения запроса.

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

3
Просто хочу заметить, что конструкция WHERE NOT EXISTS () в значительной степени идентична с «точки зрения эффективности», но, на мой взгляд, намного легче читать / понимать. С другой стороны, мои знания ограничены MSSQL, и я не могу поклясться, верно ли это на других платформах.
Дероби

Я только что попробовал этот тип сравнения на днях, когда NOT IN () имел список из нескольких сотен идентификаторов, и не было никакой разницы между ним и присоединенной версией запроса. Возможно, это имеет значение, когда вы встаете на тысячи или миллиарды.
Баттл Буткус

18

Вы можете использовать концепцию множественных запросов в ключевом слове FROM. Позвольте мне показать вам один пример:

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

Вы можете использовать столько таблиц, сколько захотите. Используйте внешние объединения и объединения там, где это необходимо, даже внутри подзапросов таблиц.

Это очень простой способ задействовать столько таблиц и полей.


8

Надеюсь, что это заставит его находить таблицы, когда вы читаете эту вещь:

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    |                |
+-------+-------------+------+-----+---------+----------------+
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.