Часть 1 - Объединения и Союзы
Этот ответ охватывает:
- Часть 1
- Часть 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
таблицы, которую мы имеем. Теперь мы знаем, что таблица моделей имеет идентификатор 1
for, 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
не будет. Это означает, что при использовании union
over есть небольшой скачок производительности, 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)
Если вы хотите выполнить пересечение двух разных таблиц, используя базу данных, которая по своей сути не поддерживает запрос пересечения, вам нужно будет создать соединение для каждого столбца таблиц.
Хорошо, я нашел этот пост очень интересным, и я хотел бы поделиться некоторыми своими знаниями по созданию запроса. Спасибо за этот Fluffeh . Другие, кто может прочитать это и могут почувствовать, что я неправ, на 101% свободны редактировать и критиковать мой ответ. ( Честно говоря, я очень благодарен за исправление моей ошибки. )
Я буду публиковать некоторые часто задаваемые вопросы в
MySQL
теге.Трюк № 1 ( строки, соответствующие нескольким условиям )
Учитывая эту схему
ВОПРОС
Найти все фильмы , которые принадлежат , по крайней мере , как
Comedy
иRomance
категории.Решение
Этот вопрос иногда может быть очень сложным. Может показаться, что такой запрос будет ответом:
SQLFiddle Demo
что, безусловно, очень неправильно, потому что это не дает результата . Объяснение в том , что есть только один действительное значение
CategoryName
на каждой строке . Например, первое условие возвращает true , второе условие всегда false. Таким образом, с помощьюAND
оператора оба условия должны выполняться; в противном случае это будет ложным. Другой запрос такой,SQLFiddle Demo
и результат все еще неверен, потому что он соответствует записи, в которой есть хотя бы одно совпадение
categoryName
. Реальное решение было бы путем подсчета количества записей экземпляров на фильм . Номер экземпляра должен соответствовать общему количеству значений, указанных в условии.SQLFiddle Demo (ответ)
Трюк № 2 ( максимальная запись для каждой записи )
Данная схема,
ВОПРОС
Найдите последнюю версию для каждого программного обеспечения. Показать следующие столбцы:
SoftwareName
,Descriptions
,LatestVersion
( из колонки VersionNo ),DateReleased
Решение
Некоторые разработчики SQL по ошибке используют
MAX()
агрегатную функцию. Они имеют тенденцию создавать, как это,SQLFiddle Demo
( большинство СУБД генерирует синтаксическую ошибку в этом случае из-за того, что не указаны некоторые неагрегированные столбцы в
group by
предложении ), результат выдает корректный результатLatestVersion
для каждого программного обеспечения, но, очевидноDateReleased
, неверный.MySQL
не поддерживаетWindow Functions
иCommon Table Expression
еще некоторые СУБД уже делают. Обходной путь для этой проблемы заключается в создании переменной,subquery
которая получает индивидуальный максимумversionNo
для каждого программного обеспечения, а затем объединяется с другими таблицами.SQLFiddle Demo (ответ)
Так вот и все. Я буду публиковать еще один, как только я вспоминаю другие часто задаваемые вопросы по
MySQL
тегу. Спасибо за чтение этой маленькой статьи. Я надеюсь, что вы хотя бы получили хоть немного знаний об этом.ОБНОВЛЕНИЕ 1
Трюк № 3 ( Поиск последней записи между двумя идентификаторами )
Данная схема
ВОПРОС
Найти последний разговор между двумя пользователями.
Решение
SQLFiddle Demo
источник
comedy
иromance
.Having
тогда не подходит ..distinct
в предложение имение SQLFiddle Demo : DЧасть 2 - Подзапросы
Хорошо, теперь босс снова ворвался - я хочу список всех наших автомобилей с маркой и общее количество этой марки у нас!
Это прекрасная возможность использовать следующий трюк в нашем наборе SQL вкусностей - подзапрос. Если вы не знакомы с этим термином, подзапрос - это запрос, который выполняется внутри другого запроса. Есть много разных способов их использования.
Для нашего запроса, давайте сначала соберем простой запрос, в котором будут перечислены все автомобили и марки:
Теперь, если мы хотим просто подсчитать количество автомобилей, отсортированных по марке, мы можем, конечно, написать это:
Итак, мы должны иметь возможность просто добавить функцию count к нашему исходному запросу, верно?
К сожалению, нет, мы не можем этого сделать. Причина в том, что когда мы добавляем идентификатор автомобиля (столбец a.ID), мы должны добавить его в группу, так что теперь, когда работает функция подсчета, для каждого идентификатора соответствует только один идентификатор.
Однако здесь мы можем использовать подзапрос - фактически мы можем сделать два совершенно разных типа подзапроса, которые будут возвращать те же результаты, которые нам нужны для этого. Первый - просто поместить подзапрос в
select
предложение. Это означает, что каждый раз, когда мы получаем строку данных, подзапрос будет выполняться, получать столбец данных и затем вставлять его в наш ряд данных.И Бэм !, это сделало бы нас. Однако, если вы заметили, этот подзапрос должен будет выполняться для каждой строки данных, которую мы возвращаем. Даже в этом небольшом примере у нас всего пять разных марок автомобилей, но подзапрос выполнялся одиннадцать раз, поскольку у нас есть одиннадцать строк данных, которые мы возвращаем. Так что в данном случае это не самый эффективный способ написания кода.
Для другого подхода давайте запустим подзапрос и представим, что это таблица:
Итак, у нас есть одинаковые результаты (упорядоченные немного по-другому - кажется, что база данных хотела вернуть результаты, упорядоченные по первому столбцу, который мы выбрали на этот раз) - но те же правильные числа.
Итак, в чем разница между ними - и когда мы должны использовать каждый тип подзапроса? Во-первых, давайте удостоверимся, что мы понимаем, как работает этот второй запрос. Мы выбрали две таблицы в
from
предложении нашего запроса, а затем написали запрос и сказали базе данных, что на самом деле это была таблица, - что база данных вполне устраивает. Там могут быть некоторые преимущества использования этого метода (а также некоторые ограничения). Прежде всего, этот подзапрос выполнялся один раз . Если бы наша база данных содержала большой объем данных, вполне может быть значительное улучшение по сравнению с первым методом. Однако, поскольку мы используем это как таблицу, мы должны добавить дополнительные строки данных, чтобы они могли быть присоединены к нашим строкам данных. Мы также должны быть уверены, что есть достаточностроки данных, если мы собираемся использовать простое соединение, как в запросе выше. Если вы помните, объединение будет вытягивать только те строки, которые имеют совпадающие данные с обеих сторон объединения. Если мы не будем осторожны, это может привести к тому, что действительные данные не будут возвращены из нашей таблицы автомобилей, если в этом подзапросе не было подходящей строки.Теперь, оглядываясь на первый подзапрос, также есть некоторые ограничения. Поскольку мы собираем данные обратно в одну строку, мы можем ТОЛЬКО вернуть одну строку данных. Подзапросы , используемые в
select
пункте запроса очень часто используют только агрегатную функцию , такие какsum
,count
,max
или другую аналогичную совокупную функцию. Им не нужно , но так часто пишут.Итак, прежде чем мы продолжим, давайте кратко рассмотрим, где еще мы можем использовать подзапрос. Мы можем использовать его в
where
предложении - теперь этот пример немного надуманный, поскольку в нашей базе данных есть лучшие способы получения следующих данных, но если посмотреть только на пример, давайте посмотрим:Это возвращает нам список идентификаторов брендов и названий брендов (второй столбец добавлен только для того, чтобы показать нам бренды), которые содержат букву
o
в названии.Теперь мы можем использовать результаты этого запроса в предложении where:
Как вы можете видеть, несмотря на то, что подзапрос возвращал три идентификатора бренда, наша таблица автомобилей содержала записи только для двух из них.
В этом случае, для более подробной информации, подзапрос работает так, как если бы мы написали следующий код:
Опять же, вы можете увидеть, как подзапрос по сравнению с ручным вводом изменил порядок строк при возврате из базы данных.
Пока мы обсуждаем подзапросы, давайте посмотрим, что еще мы можем сделать с подзапросом:
select
предложении, некоторые вfrom
предложении и еще пару вwhere
предложении - просто помните, что каждый введенный вами запрос усложняет ваш запрос и может занять больше времени. выполнить.Если вам нужно написать какой-то эффективный код, может быть полезно написать запрос несколькими способами и посмотреть (либо по времени, либо с помощью плана объяснения), какой запрос является оптимальным для получения ваших результатов. Первый способ, который работает, не всегда может быть лучшим способом сделать это.
источник
Часть 3 - хитрости и эффективный код
MySQL в () эффективность
Я думал, что добавлю несколько дополнительных битов, для подсказок и трюков, которые придумали.
Один вопрос, который я вижу, довольно справедливый: как мне получить несоответствующие строки из двух таблиц, и я вижу ответ, наиболее часто принимаемый как что-то вроде следующего (основанное на нашей таблице автомобилей и марок - в которой Холден указан как бренд, но не отображается в таблице автомобилей):
И да, это будет работать.
Однако это не эффективно в некоторых базах данных. Вот ссылка на вопрос о переполнении стека, задающий этот вопрос , и вот отличная углубленная статья, если вы хотите разобраться в мелочах.
Краткий ответ: если оптимизатор не справляется с этим эффективно, может быть гораздо лучше использовать запрос, подобный следующему, чтобы получить несопоставленные строки:
Обновить таблицу с той же таблицей в подзапросе
Аааа, еще одна старая, но хорошая вещь - старая. Вы не можете указать целевые таблицы «бренды» для обновления в предложении FROM .
MySQL не позволит вам выполнить
update...
запрос с вложенным выбором для той же таблицы. Теперь вы можете подумать, почему бы просто не вставить это в пункт where, верно? Но что, если вы хотите обновить только строку сmax()
датой среди множества других строк? Вы не можете точно сделать это в предложении where.Итак, мы не можем этого сделать, а? Ну, не совсем так. Существует хитрый обходной путь, о котором удивительно большое количество пользователей не знает, хотя он включает в себя некоторые хакерские действия, на которые вам нужно будет обратить внимание.
Вы можете вставить подзапрос в другой подзапрос, который создает достаточный промежуток между двумя запросами, чтобы он работал. Однако обратите внимание, что может быть безопаснее прикрепить запрос в транзакции - это предотвратит любые другие изменения в таблицах во время выполнения запроса.
источник
Вы можете использовать концепцию множественных запросов в ключевом слове FROM. Позвольте мне показать вам один пример:
Вы можете использовать столько таблиц, сколько захотите. Используйте внешние объединения и объединения там, где это необходимо, даже внутри подзапросов таблиц.
Это очень простой способ задействовать столько таблиц и полей.
источник
Надеюсь, что это заставит его находить таблицы, когда вы читаете эту вещь:
jsfiddle
источник