Подзапрос против производительности соединений

У меня есть две таблицы Sites и ScannedItems. Сайты содержат около 15 000 строк, а ScannedItems — около 60 миллионов строк. Следующий запрос занимает около 6 минут с индексами CountUniqueRoleAssignments, Modified и siteid. Можно ли это как-то оптимизировать? Является ли какое-то соединение быстрее, чем подзапросы или любые другие подсказки?

select 
    *,
    (select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.CountUniqueRoleAssignments>0) as CountUniquePermissions,
    (select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.Modified<DATEADD (day, -30 ,GETDATE())) as CountNotModified30Days
from sites
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
0
65
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я бы, вероятно, написал этот запрос, используя соединения:

SELECT
    s.siteid,
    COALESCE(si.CountUniquePermissions, 0) AS CountUniquePermissions,
    COALESCE(si.CountNotModified30Days, 0) AS CountNotModified30Days
FROM sites s
LEFT JOIN
(
    SELECT siteid,
           COUNT(CASE WHEN CountUniqueRoleAssignments > 0 THEN 1 END)
               AS CountUniquePermissions,
           COUNT(CASE WHEN Modified < DATEADD (day, -30, GETDATE()) THEN 1 END)
               AS CountNotModified30Days
    FROM ScannedItems
    GROUP BY siteid
) si
    ON si.siteid = s.siteid
ORDER BY
    s.siteid;

В приведенном выше запросе нет предложений WHERE или HAVING, поэтому я не вижу очевидного способа его дальнейшей настройки с использованием индексов. Но у него, по крайней мере, есть потенциальное преимущество перед вашим текущим запросом, заключающееся в том, что он не включает поведение N^2 с коррелированными подзапросами в предложении select.

Только что проверил НАМНОГО быстрее. 36 сек. Большое спасибо!

Thomas Segato 16.12.2020 08:03

@ThomasSegato Отличные новости! Обратите внимание, что вы можете упростить ответ Попая, если вы действительно хотите выбрать только столбец siteid из таблицы sites. Если вам нужны и другие столбцы, придерживайтесь моего ответа.

Tim Biegeleisen 16.12.2020 08:07

Вы можете использовать LEFT JOIN и условную агрегацию следующим образом:

select 
    S.siteid,
    COUNT(CASE WHEN SI.CountUniqueRoleAssignments > 0 THEN 1 END) as CountUniquePermissions,
    COUNT(CASE WHEN SI.Modified<DATEADD (day, -30 ,GETDATE()) THEN 1 END ) as CountNotModified30Days
from sites S
LEFT JOIN ScannedItems SI ON SI.siteid = S.siteid
GROUP BY S.SITEID

При любом вопросе производительности вы должны протестировать код SQL на своих данных. Для этого запроса:

select s.*,
       (select COUNT(*)
        from ScannedItems si
        where si.siteid = s.siteid and si.CountUniqueRoleAssignments > 0
       ) as CountUniquePermissions,
       (select COUNT(*)
        from ScannedItems si
        where si.siteid = s.siteid and si.Modified < DATEADD(day, -30 ,GETDATE())
       ) as CountNotModified30Days
from sites s;

Я бы рекомендовал два индекса:

  • ScannedItems(siteid, CountUniqueRoleAssignments)
  • ScannedItems(siteid, Modified).

Я ожидаю, что с этими двумя индексами это будет быстрее, чем любая альтернатива, использующая агрегацию. Почему? Агрегация — довольно дорогая операция. Коррелированные подзапросы могут использовать для обработки только индексы.

Это сканирование двух индексов (по сравнению с одной агрегацией), поэтому не гарантируется на 100%, будет ли это быстрее, поэтому вам следует протестировать его. Однако, если у вас есть фильтр в таблице sites, подход коррелированного подзапроса часто намного быстрее, потому что для расчета используются только сайты, возвращаемые запросом.

Другие вопросы по теме