2013-06-28 142 views
3

我想使用django ORM的聚合功能在MSSQL 2008R2數據庫上運行查詢,但我不斷收到超時錯誤。下面是失敗的查詢(由django生成)。我試過運行它指揮SQL管理工作室,它的工作原理,但需要3.5分鐘超時運行SQL查詢

它看起來是聚合在一堆領域,它不需要,但我不會有真的會導致它需要很長時間。數據庫也不是那麼大,auth_user有9條記錄,ticket_ticket有1210和ticket_watchers有1876.有什麼我失蹤?

SELECT 
    [auth_user].[id], 
    [auth_user].[password], 
    [auth_user].[last_login], 
    [auth_user].[is_superuser], 
    [auth_user].[username], 
    [auth_user].[first_name], 
    [auth_user].[last_name], 
    [auth_user].[email], 
    [auth_user].[is_staff], 
    [auth_user].[is_active], 
    [auth_user].[date_joined], 
    COUNT([tickets_ticket].[id]) AS [tickets_captured__count], 
    COUNT(T3.[id]) AS [assigned_tickets__count], 
    COUNT([tickets_ticket_watchers].[ticket_id]) AS [tickets_watched__count] 
FROM 
    [auth_user] 
    LEFT OUTER JOIN [tickets_ticket] ON ([auth_user].[id] = [tickets_ticket].[capturer_id]) 
    LEFT OUTER JOIN [tickets_ticket] T3 ON ([auth_user].[id] = T3.[responsible_id]) 
    LEFT OUTER JOIN [tickets_ticket_watchers] ON ([auth_user].[id] = [tickets_ticket_watchers].[user_id]) 
GROUP BY 
    [auth_user].[id], 
    [auth_user].[password], 
    [auth_user].[last_login], 
    [auth_user].[is_superuser], 
    [auth_user].[username], 
    [auth_user].[first_name], 
    [auth_user].[last_name], 
    [auth_user].[email], 
    [auth_user].[is_staff], 
    [auth_user].[is_active], 
    [auth_user].[date_joined] 
HAVING 
    (COUNT([tickets_ticket].[id]) > 0 OR COUNT(T3.[id]) > 0) 

編輯:

下面是相關的索引(不包括在查詢中不使用):

auth_user.id      (PK) 
auth_user.username     (Unique) 
tickets_ticket.id     (PK) 
tickets_ticket.capturer_id 
tickets_ticket.responsible_id 
tickets_ticket_watchers.id   (PK) 
tickets_ticket_watchers.user_id 
tickets_ticket_watchers.ticket_id 

編輯2:

位的後實驗中,我發現以下查詢是導致執行速度慢的最小問題:

SELECT 
    COUNT([tickets_ticket].[id]) AS [tickets_captured__count], 
    COUNT(T3.[id]) AS [assigned_tickets__count], 
    COUNT([tickets_ticket_watchers].[ticket_id]) AS [tickets_watched__count] 
FROM 
    [auth_user] 
    LEFT OUTER JOIN [tickets_ticket] ON ([auth_user].[id] = [tickets_ticket].[capturer_id]) 
    LEFT OUTER JOIN [tickets_ticket] T3 ON ([auth_user].[id] = T3.[responsible_id]) 
    LEFT OUTER JOIN [tickets_ticket_watchers] ON ([auth_user].[id] = [tickets_ticket_watchers].[user_id]) 
GROUP BY 
    [auth_user].[id] 

奇怪的是,如果我在上面註釋掉任意兩條線,它在更短的運行是1秒,但它似乎並沒有問題我刪除哪些行(雖然很明顯,我無法刪除沒有也刪除相關SELECT行的連接)。

編輯3:

其產生這種情況的Python代碼是:

User.objects.annotate(
    Count('tickets_captured'), 
    Count('assigned_tickets'), 
    Count('tickets_watched') 
) 

一看執行計劃顯示,SQL服務器第一次做的所有表中的交叉聯接,產生約2.8億行,以及6Gb的數據。我認爲這是問題所在,但爲什麼會發生?

+0

你有什麼樣的索引在桌子上? –

+0

@NenadZivkovic無論django加入了什麼 - 但好點,我會檢查它們。 – aquavitae

+0

你說得對,它需要很長時間。我會得到執行計劃來看看它,併爲io和時間設置統計信息,以查看是什麼阻止了它。你還可以發佈導致這個查詢的代碼嗎?查詢對我來說沒有多大意義。 –

回答

1

SQL Server正在做它被要求做的事情。不幸的是,Django沒有爲你想要的生成正確的查詢。它看起來像你需要統計不同,而不是僅僅數:Django annotate() multiple times causes wrong answers

至於爲什麼查詢這樣工作:查詢說,加入四個表在一起。因此,如果作者有2張拍攝的票據,3張指定票券和4張觀看票券,則加入將返回2 * 3 * 4票據,每張票據組合一張。不同的部分將刪除所有重複項。

0

這是怎麼回事?

SELECT auth_user.*, 
    C1.tickets_captured__count 
    C2.assigned_tickets__count 
    C3.tickets_watched__count 

FROM 
auth_user 
LEFT JOIN 
(SELECT capturer_id, COUNT(*) AS tickets_captured__count 
    FROM tickets_ticket GROUP BY capturer_id) AS C1 ON auth_user.id = C1.capturer_id 
LEFT JOIN 
(SELECT responsible_id, COUNT(*) AS assigned_tickets__count 
    FROM tickets_ticket GROUP BY responsible_id) AS C2 ON auth_user.id = C2.responsible_id 
LEFT JOIN 
(SELECT user_id, COUNT(*) AS tickets_watched__count 
    FROM tickets_ticket_watchers GROUP BY user_id) AS C3 ON auth_user.id = C3.user_id 

WHERE C1.tickets_captured__count > 0 OR C2.assigned_tickets__count > 0 
--WHERE C1.tickets_captured__count is not null OR C2.assigned_tickets__count is not null -- also works (I think with beter performance)