агрегация при проблемах с множественным левым соединением (SQL SERVER) - PullRequest
1 голос
/ 13 февраля 2012

У меня проблема с производительностью относительно простого запроса, который выполняется более 30 минут:

SELECT   P.pID
    ,COUNT(T1.ID) AS NB1
    ,COUNT(T2.ID) AS NB2
    ,COUNT(T3.ID) AS NB3
    ,COUNT(T4.ID) AS NB4
    ,COUNT(T5.ID) AS NB5

FROM MainTable P 

LEFT OUTER JOIN Table1 T1 ON  P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON  P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON  P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON  P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON  P.pID = T5.pID

GROUP BY P.pID

Где каждый запрос будет отвечать за несколько мс:

отл.

  SELECT   P.pID
        ,COUNT(T1.ID) AS NB1

    FROM MainTable P 
    LEFT OUTER JOIN Table1 T1 ON  P.pID = T1.pID

    GROUP BY P.pID

Если я не использую агрегацию (COUNT или что-то еще), запрос выполняется за несколько мс: ех. ВЫБЕРИТЕ P.pID

FROM MainTable P 

LEFT OUTER JOIN Table1 T1 ON  P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON  P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON  P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON  P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON  P.pID = T5.pID

GROUP BY P.pID

Очевидно, что все индексы установлены и т. Д. Единственный элемент «замедления» - это то, что pID - это varchar (50), но я не могу его изменить, и, на мой взгляд, это не главная проблема.

Я использовал обходной путь, включая union all, который прекрасно работает, но мне действительно интересно, почему они такие длинные и как я могу оптимизировать их, так как агрегация по нескольким левым соединениям - действительно распространенная вещь в проекте отчетов и не должна быть такой медленной.

спасибо за вашу помощь.

[EDIT] спасибо ARION Я получил хороший запрос, работающий действительно хорошо.

Но моя главная задача - понять, что не так в ядре sql, пишущем запрос с несколькими левыми соединениями.

Таблицы descr будут:

Table P (500 rows) 
pID varchar(50) NOT NULL as primary key
p.* doesn't matter 

Table Tn (between 2000 and 8000 rows)
Tn.ID int NOT NULL as primary key 
pID varchar(50) NOT NULL as Foreign key

[РЕДАКТИРОВАТЬ] Благодаря Erland Sommarskog на social.msdn.microsoft.com, которые указывают мне мою ошибку анализа. - подробно об ответе

Имейте в виду:
LEFT JOIN формирует декартово произведение

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

спасибо

Ответы [ 2 ]

3 голосов
/ 13 февраля 2012

Может быть, что-то вроде этого:

SELECT
    P.pID,
    (SELECT COUNT(*) FROM Table1 T1 WHERE P.pID = T1.pID) AS NB1,
    (SELECT COUNT(*) FROM Table2 T2 WHERE P.pID = T2.pID) AS NB2,
    (SELECT COUNT(*) FROM Table3 T3 WHERE P.pID = T3.pID) AS NB3,
    (SELECT COUNT(*) FROM Table4 T4 WHERE P.pID = T4.pID) AS NB4,
    (SELECT COUNT(*) FROM Table5 T5 WHERE P.pID = T5.pID) AS NB5

FROM MainTable P 
1 голос
/ 13 февраля 2012

Вы также можете переписать запрос, сначала сгруппировав (в подзапросах), а затем присоединившись:

SELECT
    P.pID,
    T1.NB1,
    T2.NB2,
    T3.NB3,
    T4.NB4,
    T5.NB5
FROM MainTable P 
  LEFT JOIN
    (SELECT pID, COUNT(*) AS NB1 FROM Table1 GROUP BY pID) AS T1
    ON T1.pID = P.pID
  LEFT JOIN
    (SELECT pID, COUNT(*) AS NB2 FROM Table2 GROUP BY pID) AS T2
    ON T2.pID = P.pID
  LEFT JOIN
    (SELECT pID, COUNT(*) AS NB3 FROM Table3 GROUP BY pID) AS T3
    ON T3.pID = P.pID
  LEFT JOIN
    (SELECT pID, COUNT(*) AS NB4 FROM Table4 GROUP BY pID) AS T4
    ON T4.pID = P.pID
  LEFT JOIN
    (SELECT pID, COUNT(*) AS NB5 FROM Table5 GROUP BY pID) AS T5
    ON T5.pID = P.pID

Это было бы полезно, если вы хотите включить в результаты другие агрегаты, кроме COUNT(*), без необходимости запуска более коррелированных подзапросов.

...