SQL запит повертає дані з декількох таблиць


434

Я хотів би знати наступне:

  • як отримати дані з декількох таблиць у моїй базі даних?
  • які типи методів існують для цього?
  • що таке об'єднання та спілки та чим вони відрізняються один від одного?
  • Коли я повинен використовувати кожен із них порівняно з іншими?

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

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

Відповіді охоплюють наступне:

  1. Частина 1 - Приєднання та об'єднання
  2. Частина 2 - Підзапити
  3. Частина 3 - Прийоми та ефективний кодекс
  4. Частина 4 - Підзапити в пункті "З"
  5. Частина 5 - Змішана сумка фокусів Джона

Відповіді:


469

Частина 1 - Приєднання та об'єднання

Ця відповідь охоплює:

  1. Частина 1
  2. Частина 2
    • Підзапити - що це таке, де їх можна використовувати і на що слідкувати
    • Картезіан приєднується до AKA - О, нещастя!

Існує ряд способів отримання даних з декількох таблиць у базі даних. У цій відповіді я буду використовувати синтаксис приєднання 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)

Таблиця брендів визначає різні марки автомобілів, на яких може бути продано кар'єр.

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)

І нарешті, зв’язати всі ці інші таблиці, таблицю, яка з’єднує все разом. Поле ID - це насправді унікальний номер партії, який використовується для ідентифікації автомобілів.

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таблиці, який ми маємо. Тепер ми знаємо , що таблиця моделей має ідентифікатор 1для 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

Тепер повернемося до запиту. Як ви бачите, у нас є необхідна нам інформація, але ми також маємо інформацію, яку не просили, тому нам потрібно включити пункт де у заяві, щоб отримати лише Спортивні автомобілі, як просили. Оскільки я віддаю перевагу методу псевдоніму таблиці, а не використовувати назви таблиць знову і знову, я буду дотримуватися його з цього моменту.

Зрозуміло, нам потрібно додати в наш запит пункт де. Ми можемо ідентифікувати спортивні автомобілі за 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 .

Це, однак, дає нам чудову привід розглянути два різні способи досягнення цього. Ми можемо додати ще одну умову до цього пункту:

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не буде. Це означає, що при використанні unionнад ними є невеликий показник ефективності, union allале результати, можливо, того варті - я не буду спекулювати на цьому подібних речах.

У цій примітці, можливо, варто зазначити деякі додаткові замітки тут.

  • Якщо ми хотіли замовити результати, ми можемо використовувати order byпсевдонім, але ви більше не можете використовувати псевдонім. У вищезазначеному запиті додавання символу 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але майже всі бази даних дозволять вам створити перехрестя за допомогою з'єднання (або добре структурованої де-небудь заяви).

Перетин - це тип з'єднання, дещо схожий на 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.
MAQS

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

що, безумовно, дуже неправильно, оскільки не дає результату . Поясненням цього є те, що CategoryNameу кожному рядку є лише одне дійсне значення . Наприклад, перша умова повертає істину , а друга умова завжди хибна. Таким чином, за допомогою ANDоператора обидві умови повинні бути істинними; інакше це буде помилково. Інший запит такий:

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

Демонстрація SQLFiddle

і результат все ще неправильний, оскільки він відповідає запису, який має принаймні одну відповідність на 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 (відповідь)


Трюк №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

( більшість RDBMS створює синтаксичну помилку з цього приводу через невказівку деяких неагрегованих стовпців у 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 (відповідь)


Так це було. Я опублікую ще одну, як тільки згадаю будь-який інший FAQ на 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


Дивовижно! Застереження Івана, ваше перше рішення працює лише тому, що існує два обмеження на двох полях. Ви могли б скористатися більш загальним рішенням для вирішення поширеної проблеми. На мою думку, єдиним рішенням є вибір індивідуальних виборів для comedyта romance. Havingне підходить тоді ..
nawfal

@nawfal не дуже, якщо обмеження унікальності не додано, то вам необхідно додати distinctна має п SQLFiddle Demo : D
John Woo

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

Отже, ми повинні мати можливість просто додати функцію підрахунку до нашого оригінального запиту?

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), ми повинні додавати його до групи шляхом - тому зараз, коли функція підрахунку працює, існує лише один ідентифікатор, що відповідає одному 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в назві.

Тепер ми могли б використовувати результати цього запиту в пункті, де це:

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...запит із підселектором у тій самій таблиці. Тепер, ви можете подумати, чому б просто не вдарити його в пункт «де»? Але що робити, якщо ви хочете оновити лише рядок із max()датою, яка складається з ряду інших рядків? Ви не можете точно зробити це в пункті де.

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
Хочу лише зазначити, що конструкція «БОГО НІМАЄ» () майже однакова з «точки зору ефективності», але, на мій погляд, набагато простіше читати / розуміти. Знову ж таки, мої знання обмежуються MSSQL, і я не можу обіцяти, якщо те саме стосується інших платформ.
деробій

Я просто спробував цей тип порівняння днями, коли NOT IN () мав список близько декількох сотень ідентифікаторів, і різниці між ним та версією запиту не було. Можливо, це має значення, коли ти встаєш на тисячі чи мільярди.
Buttle Butkus

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.