MySQL запрос - производная таблица и левое соединение, где - PullRequest
1 голос
/ 19 июня 2020

Учитывая posts и posts_tags:

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    external_id varchar(10) UNIQUE,
    title varchar(255),
    body TEXT
)

CREATE TABLE posts_tags (
    id SERIAL PRIMARY KEY,
    post_external_id varchar(10) REFERENCES posts(external_id),
    tag varchar(255),
    UNIQUE KEY (post_external_id, tag)
)

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

Существует get-post-by-external-id API, который вернет сообщение с его тегами, идентифицированными external_id сообщения. Учитывая этот вариант использования, мне интересно, какой из приведенных ниже запросов выполняется быстрее и потребляет меньше ресурсов?

Производная таблица

SELECT posts.*, posts_tags.tag 
FROM (SELECT * FROM posts WHERE external_id = ?) AS posts 
LEFT JOIN posts_tags ON posts.external_id = posts_tags.post_external_id

Левое соединение, где

SELECT posts.*, posts_tags.tag 
FROM posts 
LEFT JOIN posts_tags ON posts.external_id = posts_tags.post_external_id
WHERE posts.external_id = ?

I ' м не уверен, но я думаю, что WHERE фильтрует промежуточные результаты после того, как происходит LETF JOIN?

1 Ответ

1 голос
/ 19 июня 2020

Нет необходимости в подзапросе. Движок не оценивает / не выполняет запрос в том же порядке, в котором вы пишете. Оптимизатор сгенерирует максимально оптимальный «путь выполнения» (хотя он не работает идеально). Однако для этого конкретного случая это задокументировано в Оптимизация предложения WHERE :

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

Итак, ваше предположение:

I guess the WHERE filters the intermediate results after the LETF JOIN happens?

неверно. Учитывая индекс на posts.external_id, движок будет только «касаться» соответствующей строки (строк) и выполнять соединение с posts_tags только для этой / этих строк.

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

Я бы не стал использовать этот запрос по другой причине. Вы получаете дублирование данных поста для каждого связанного тега. Предположим, у вас есть сообщение, содержащее 10 КБ текста и 10 тегов. Вы получите 10 строк (по одной на тег) с одинаковым текстом и получите вместе 100 КБ данных. Затем вам нужно будет написать код для «дедупликации» данных.

Я бы предпочел выполнить два отдельных запроса:

SELECT posts.*
FROM posts 
WHERE posts.external_id = ?

и

SELECT posts_tags.tag 
FROM posts_tags
WHERE posts_tags.post_external_id = ?

и " объединить "результат в код приложения

или объединить теги с помощью GROUP_CONCAT ()

SELECT posts.*, GROUP_CONCAT(posts_tags.tag) as tags
FROM posts 
LEFT JOIN posts_tags ON posts.external_id = posts_tags.post_external_id
WHERE posts.external_id = ?
GROUP BY posts.external_id

или с помощью JSON_ARRAYAGG ()

SELECT posts.*, JSON_ARRAYAGG(posts_tags.tag) as tags
FROM posts 
LEFT JOIN posts_tags ON posts.external_id = posts_tags.post_external_id
WHERE posts.external_id = ?
GROUP BY posts.external_id

Анализ списка с разделителями-запятыми или JSON массив должен быть простым на любом языке приложения.

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