3

django ORM の集計機能を使用して MSSQL 2008R2 データベースでクエリを実行しようとしていますが、タイムアウト エラーが発生し続けます。失敗するクエリ (django によって生成された) は以下のとおりです。SQL管理スタジオを指示して実行しようとしましたが、動作しますが、3.5分かかります

必要のない一連のフィールドを集約しているように見えますが、実際にはそれほど時間がかかるはずですが、そうではありません。データベースもそれほど大きくなく、auth_user9 件のレコードがticket_ticketあり、1210 件、ticket_watchers1876 件あります。足りないものはありますか?

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]

奇妙なことに、上記の任意の 2 行をコメント アウトすると、1 秒未満で実行されますが、どの行を削除しても問題ないように思われます (ただし、関連する行を削除せずに結合を削除することはできません)。行を選択します)。

編集3:

これを生成した python コードは次のとおりです。

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

実行計画を見ると、SQL Server が最初にすべてのテーブルでクロス結合を行っていることがわかります。その結果、約 2 億 8000 万行と 6Gb のデータが生成されます。ここに問題があると思いますが、なぜそれが起こっているのですか?

4

2 に答える 2

1

SQL Server は、要求されたことを正確に実行しています。残念ながら、Django は必要なクエリを正しく生成していません。カウントするだけでなく、個別にカウントする必要があるようです: Django annotate() 複数回は間違った答えを引き起こします

クエリがそのように機能する理由について: クエリは、4 つのテーブルを結合するように指示します。たとえば、作成者が 2 つのキャプチャ済みチケット、3 つの割り当てられたチケット、4 つの監視済みチケットを持っているとします。この結合は、チケットの組み合わせごとに 1 つずつ、2*3*4 チケットを返します。個別の部分はすべての重複を削除します。

于 2013-06-28T15:09:36.347 に答える
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)
于 2013-06-28T14:14:07.010 に答える