С помощью предикатов равенства в столбцах external_id
и external_context
в предложении WHERE MySQL может эффективно использовать индекс ... когда эти предикаты указывают подмножество строк, которые могут удовлетворить запрос.
Но с добавлением OR
к предложению WHERE
теперь строки, возвращаемые из c
, имеют , а не , ограниченные значениями external_id
и external_content
. Теперь возможно, что строки с другими значениями этих столбцов могут быть возвращены;строк с любыми значениями этих столбцов.
И это сводит на нет большое преимущество использования операции сканирования диапазона индекса ... очень быстро устраняя обширные полосы строк изсчитаетсяДа, сканирование диапазона индекса используется для быстрого поиска строк. Это правда. Но суть в том, что операция сканирования диапазона использует индекс для быстрого обхода миллионов и миллионов строк, которые невозможно вернуть.
Это не характерно для MariaDB 10.3. Мы будем наблюдать такое же поведение в MariaDB 10.2, MySQL 5.7, MySQL 5.6.
Я подвергаю сомнению операцию соединения: необходимо ли возвращать несколько копий строкс c
при наличии нескольких совпадающих строк из reply_c
? Или же спецификация просто возвращает отдельные строки из c
?
Мы можем рассматривать требуемый набор результатов как две части.
1) строки из app_contents
с равенствомПредикаты external_id
и external_context
SELECT c.*
FROM app_comments c
WHERE c.external_id = '840774'
AND c.external_context = 'deals'
ORDER
BY c.external_id
, c.external_context
, c.reply_to
, c.date
Для оптимальной производительности (исключая рассмотрение индекса покрытия из-за *
в списке SELECT), такой индекс может использоваться для удовлетворения обоихоперация сканирования диапазона и порядок (исключая операцию использования файловой сортировки)
... ON app_comments (external_id, external_context, reply_to, date)
2) Вторая часть результата - это reply_to
строк, связанных с соответствующими строками
SELECT d.*
FROM app_comments d
JOIN app_comments e
ON e.id = d.reply_to
WHERE e.external_id = '840774'
AND e.external_context = 'deals'
ORDER
BY d.reply_to
, d.date
Тот же индекс, рекомендованный ранее, можно использовать для доступа к строкам в e
(операция сканирования диапазона). В идеале этот индекс также должен включать столбец id
. Нашим лучшим вариантом, вероятно, является изменение индекса для включения столбца id
после date
... ON app_comments (external_id, external_context, reply_to, date, id)
Или, для эквивалентной производительности, за счет дополнительного индекса, мы могли бы определить индекс следующим образом:
... ON app_comments (external_id, external_context, id)
Для доступа к строкам из d
со сканированием диапазона нам, вероятно, понадобится индекс:
... ON app_comments (reply_to, date)
Мы можем объединить два набора с UNION ALL
оператор набора;но есть вероятность того, что одна и та же строка будет возвращена обоими запросами. Оператор UNION
заставит уникальную сортировку удалить дублирующиеся строки. Или мы могли бы добавить условие ко второму запросу, чтобы исключить строки, которые будут возвращены первым запросом.
SELECT d.*
FROM app_comments d
JOIN app_comments e
ON e.id = d.reply_to
WHERE e.external_id = '840774'
AND e.external_context = 'deals'
HAVING NOT ( d.external_id <=> '840774'
AND d.external_context <=> 'deals'
)
ORDER
BY d.reply_to
, d.date
Объединяя две части, оберните каждую часть в набор символов, добавьте UNIONВСЕ оператор set и оператор ORDER BY в конце (вне паренов), что-то вроде этого:
(
SELECT c.*
FROM app_comments c
WHERE c.external_id = '840774'
AND c.external_context = 'deals'
ORDER
BY c.external_id
, c.external_context
, c.reply_to
, c.date
)
UNION ALL
(
SELECT d.*
FROM app_comments d
JOIN app_comments e
ON e.id = d.reply_to
WHERE e.external_id = '840774'
AND e.external_context = 'deals'
HAVING NOT ( d.external_id <=> '840774'
AND d.external_context <=> 'deals'
)
ORDER
BY d.reply_to
, d.date
)
ORDER BY `reply_to`, `date`
Для этого потребуется операция «Использование файловой сортировки» над объединенным множеством, но теперь мы получилидействительно хороший способ получить хороший план выполнения для каждой части.
У меня все еще остается вопрос о том, сколько строк мы должны вернуть, если имеется несколько совпадающих строк response_to.