Выбирайте родительскую строку, только если у нее нет детей - PullRequest
10 голосов
/ 19 июля 2011

У меня есть база данных MySQL, в которой таблица A имеет отношение «один ко многим» к таблице B, и я хотел бы выбрать все строки в таблице B, которые не имеют дочерних элементов в таблице A. Я пытался использовать

SELECT id FROM A WHERE NOT EXISTS (SELECT * FROM B WHERE B.id=A.id)

и

SELECT id FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL

Оба они кажутся медленными. Есть ли более быстрый запрос для достижения того же самого?

В случае, если это уместно, в моей базе данных таблица A содержит около 500 000 строк, а таблица B содержит от 3 до 4 миллионов строк.

Редактировать: Для таблиц в моей базе данных объяснение дает мне:

+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+
| id | select_type        | table            | type  | possible_keys | key                       | key_len | ref  | rows    | Extra                    |
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+
|  1 | PRIMARY            | frontend_form471 | index | NULL          | frontend_form471_61a633e8 | 32      | NULL |  671927 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | SchoolData       | index | PRIMARY       | PRIMARY                   | 49      | NULL | 3121110 | Using where; Using index |
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+

для

select number from frontend_form471 where not exists (select * from SchoolData where SchoolData.`f471 Application Number`=frontend_form471.number)

и

+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+
| id | select_type | table            | type  | possible_keys | key                       | key_len | ref  | rows    | Extra                                          |
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+
|  1 | SIMPLE      | frontend_form471 | index | NULL          | frontend_form471_61a633e8 | 32      | NULL |  671927 | Using index; Using temporary                   |
|  1 | SIMPLE      | SchoolData       | index | PRIMARY       | PRIMARY                   | 49      | NULL | 3121110 | Using where; Using index; Not exists; Distinct |
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+

для

select distinct number from frontend_form471 left join SchoolData on frontend_form471.number=SchoolData.`f471 Application Number` where SchoolData.`f471 Application Number` is NULL

, где в моем случае frontend_form471 - это таблица A, а SchoolData - это таблица B

Edit2: В таблице B (SchoolData) в моей базе данных id является первой частью первичного ключа, состоящего из двух частей, поэтому он проиндексирован, и в B по-прежнему есть несколько записей с одинаковым идентификатором.

Ответы [ 7 ]

9 голосов
/ 19 июля 2011
SELECT id FROM A LEFT OUTER JOIN B ON A.id=B.id WHERE B.id IS NULL

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

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

правильный путь - кэширование!попробуйте кэширование запросов и кэширование на уровне приложения, если это возможно.

конечно, вам нужны правильные индексы.

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

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

если вам действительно нужно это быстро, вы можете повторно фактурировать ваши данныеСтруктура.

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

последняя мысль: вы можете попробовать SELECT id FROM A WHERE id NOT IN (SELECT id FROM B), это может быть немного быстрее, потому что фактическое объединение не требуется, однако это также может быть медленнее, потому что поиск в наборебудет полное сканирование.Я не совсем уверен, как это будет обрабатываться, но, возможно, стоит попробовать.

1 голос
/ 19 июля 2011

Ваша индексация плохая.

Для всех форм (EXISTS, IN, LEFT JOIN) у вас должны быть индексы по id в обеих таблицах

1 голос
/ 19 июля 2011

Вы можете попробовать

SELECT id FROM A WHERE A.id NOT IN (SELECT id FROM B)

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

1 голос
/ 19 июля 2011

Это будет медленно, независимо от того, как вы на это смотрите. В худшем случае производительность будет полным перекрестным соединением, создающим 2 триллиона потенциальных совпадений (4 миллиона * 500 тысяч).

Второй, скорее всего, будет работать быстрее, так как это один запрос.

0 голосов
/ 19 июля 2011

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

0 голосов
/ 19 июля 2011

Если ваша схема примерно такая:

CREATE TABLE b(
    id int,
    value varchar(255)
)

CREATE TABLE a(
    id int,
    father_id int,
    value varchar(255)
)

Если вы хотите, чтобы все строки таблицы A, у которых нет дочернего элемента в таблице A, почему бы вам не попробовать что-то вроде:

SELECT * FROM B WHERE id NOT IN (SELECT father_id FROM A GROUP BY father_id)

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

Надеюсь, это поможет

0 голосов
/ 19 июля 2011

Обязательно укажите индекс на A.id, а другой на B.id.

То, что кажется немного странным, это то, что вы присоединяетесь к A.id с B.id. Является ли B.id внешним ключом для A или это первичный ключ для B?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...