Быстрый запрос для выбора всех записей НЕ в другой таблице в MySQL - PullRequest
2 голосов
/ 21 апреля 2011

У меня есть запрос, который выбирает таблицу узлов, а затем присоединяет к ней таблицу заголовков.Для этого сначала необходимо объединить промежуточную таблицу идентификаторов узлов и идентификаторов заголовков, что позволяет установить связь «многие ко многим» между первыми двумя таблицами.Оба соединения являются внутренними, поэтому выбираются только узлы с правильно настроенным и существующим заголовком.Я считаю, что все это должно быть чисто и эффективно - проблема заключается в следующем:

Существует также четвертая таблица, которая обеспечивает простую иерархию для узлов;node_parents.Каждая строка имеет два поля;идентификатор узла и идентификатор узла, который действует как родитель этого узла (node_id и parent_id).Некоторые узлы не имеют дочерних элементов, настроенных в этой базе данных (т. Е. Сам узел не помечен как родительский в какой-либо строке таблицы node_parents) - это узлы, которые я пытаюсь выбрать.

Дополнительным критерием для этих бездетных узлов является то, что у них настроен определенный заголовок - отсюда подзапрос, который сначала выбирается из node_titles, а затем во внутреннем соединении node_parents.У подзапроса также есть GROUP BY, потому что некоторые узлы являются родителями нескольких узлов, поэтому их node_id будет излишне появляться в результатах несколько раз.Следует также отметить, что из-за этого первичный ключ для node_parents представляет собой комбинацию node_id и parent_id.

Запрос:

SELECT  `nodes`.`node_id`,
        `titles`.`title`
FROM `nodes`
INNER JOIN `node_titles`
ON `nodes`.`node_id` = `node_titles`.`node_id`
INNER JOIN `titles`
ON `node_titles`.`title_id` = `titles`.`title_id`
WHERE `nodes`.`node_id` NOT IN
    (
    SELECT `node_titles`.`node_id`
    FROM `node_titles`
    INNER JOIN `node_parents`
    ON `node_titles`.`node_id` = `node_parents`.`parent_id`
    WHERE `node_titles`.`title_id` = 1
    GROUP BY `node_titles`.`node_id`
    )
AND `titles`.`title_id` = 1

Размеры таблиц: node = ~ 32 000 node_titles =~ 49 000 заголовков = 3 node_parents = ~ 55 000

Выполнение запроса занимает около 16 минут.Кто-нибудь может предоставить какие-либо указатели?Я попытался профилировать запрос - который не имеет длительных зависаний, но он повторяет этот цикл для каждой выбранной строки:

| executing                      | 0.000005 |
| Copying to tmp table           | 0.515815 |
| Sorting result                 | 0.000053 |
| Sending data                   | 0.000028 |

Я также пытался отключить подзапрос и использовать LEFT.ПРИСОЕДИНЯЙТЕСЬ к WHERE, foo НЕ НУЛЬ, но это все еще занимает много времени для обработки - профилировщик требует ~ 180 секунд для 'Копирования в таблицу tmp'.

В конечном счете, я подозреваю, что это может быть проблема с индексированием - ноВ любом случае, я был бы признателен за ответы, которые не ставят под сомнение реализацию запроса, если только они не преследуют возможную причину замедления (например, да, заголовки и узлы должны находиться в отношениях «многие ко многим»).Спасибо всем, и дополнительную информацию по запросу!

Ответы [ 2 ]

2 голосов
/ 21 апреля 2011

Удалите GROUP BY из подзапроса:

SELECT  nodes.node_id,
        titles.title
FROM    nodes n
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
WHERE   n.node_id NOT IN
        (
        SELECT  nti.node_id
        FROM    node_titles nti
        INNER JOIN 
                node_parents npi
        ON      npi.parent_id = nt.node_id
        WHERE   nti.title_id = 1
        )

Создайте следующие индексы:

node_titles (node_id, title_id)
titles (title_id)
node_parents (parent_id)

Обновление:

Попробуйте это:

SELECT  nodes.node_id,
        titles.title
FROM    nodes n
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
        AND nt.title_id = 1
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
WHERE   n.node_id NOT IN
        (
        SELECT  parent_id
        FROM    node_parents
        )
1 голос
/ 21 апреля 2011

MySql имеет тенденцию иметь проблемы с подзапросами в моем опыте. Попробуйте это

SELECT  nodes.node_id,
        titles.title
FROM    nodes b
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
LEFT OUTER JOIN   
        (
        SELECT  nti.node_id
        FROM    node_titles nti
        INNER JOIN 
                node_parents npi
        ON      npi.parent_id = nt.node_id
        WHERE   nti.title_id = 1
        ) ThisTable on n.node_id = ThisTable.node_id
 WHERE ThisTable.node_id is null
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...