1

SQL Server 2008 を使用しています。

複数の場所があり、それぞれに複数の部門があり、各部門にはゼロから多数のスキャンを持つことができる複数のアイテムが含まれています。各スキャンは、カットオフ タイムがある場合とない場合がある特定の操作に関連しています。各アイテムは、特定のジョブに属する特定のパッケージにも属します。各ジョブには、1 つ以上のアイテムを含む 1 つ以上のパッケージが含まれます。

+=============+                         +=============+
|  Locations  |                         |     Jobs    |
+=============+                         +=============+
      ^                                       ^
      |                                       |
+=============+     +=============+     +=============+
| Departments | <-- |    Items    | --> |   Packages  |
+=============+     +=============+     +=============+
                          ^
                          |
                    +=============+     +=============+
                    |    Scans    | --> | Operations  |
                    +=============+     +=============+

私がやろうとしているのは、場所とスキャンの日付でグループ化されたジョブのスキャンの数を取得することです。トリッキーな部分は、操作のカットオフ時間が null ではない項目ごとに、日付/時刻による最初のスキャンのみをカウントしたいということです。(注: スキャンは、テーブル内の日付/時間順ではありません。)

私が持っているクエリは正しい結果を得ていますが、ジョブのアイテム数が 75,000 程度に達すると非常に遅くなります。私は新しいサーバーを求めています -- 私たちのハードウェアが不足していることは知っています -- しかし、私がクエリで行っていることでサーバーが動かなくなっているのではないかと考えています。

実行計画から得られるわずかな情報から、クエリのコストのほとんどは、各アイテムの最初のスキャンを見つけるためのサブクエリにあるようです。Operations テーブルのインデックス (ID、Cutoff) に対してインデックス スキャン (0%) を実行し、次に遅延スプール (19%) を実行します。Scans テーブルのインデックス (ItemID、DateTime、OperationID、ID) でインデックス シーク (61%) を実行します。後続のネストされたループ (内部結合) はわずか 2% で、Top 演算子は 0% です。(入力した内容の多くを本当に理解しているわけではありませんが、できるだけ多くの情報を提供しようとしています...)

クエリは次のとおりです。

SELECT
    Departments.LocationID
    , DATEADD(dd, 0, DATEDIFF(dd, 0, Scans.DateTime))
    , COUNT(Scans.ItemID) AS [COUNT]
FROM
    Items           
    INNER JOIN Scans
        ON Scans.ID = 
    (
        SELECT TOP 1
            Scans.ID 
        FROM
            Scans
        INNER JOIN Operations
            ON Scans.OperationID = Operations.ID
        WHERE
            Operations.Cutoff IS NOT NULL
            AND Scans.ItemID = Items.ID             
        ORDER BY
            Scans.DateTime
    )
    INNER JOIN Operations
        ON Scans.OperationID = Operations.ID
    INNER JOIN Packages
        ON Items.PackageID = Packages.ID
    INNER JOIN Departments
        ON Items.DepartmentID = Departments.ID      
WHERE
    Packages.JobID = @ID        
GROUP BY
    Departments.LocationID 
    , DATEADD(dd, 0, DATEDIFF(dd, 0, Scans.DateTime));

次のような結果のサンプリングが返されます。

8   2012-06-08 00:00:00.000 11842
21  2012-06-07 00:00:00.000 502
11  2012-06-12 00:00:00.000 1841
15  2012-06-11 00:00:00.000 4314
16  2012-06-09 00:00:00.000 278
23  2012-06-12 00:00:00.000 1345
6   2012-06-06 00:00:00.000 2005
20  2012-06-08 00:00:00.000 352
14  2012-06-07 00:00:00.000 2408
8   2012-06-11 00:00:00.000 290
19  2012-06-10 00:00:00.000 85
20  2012-06-11 00:00:00.000 5484
7   2012-06-10 00:00:00.000 2389
16  2012-06-06 00:00:00.000 6762
18  2012-06-09 00:00:00.000 4473
14  2012-06-10 00:00:00.000 2364
1   2012-06-11 00:00:00.000 1531
22  2012-06-08 00:00:00.000 14534
5   2012-06-10 00:00:00.000 11908
9   2012-06-12 00:00:00.000 47
19  2012-06-07 00:00:00.000 559
7   2012-06-07 00:00:00.000 2576

実行計画は次のとおりです (元の投稿以降に何を変更したかはわかりませんが、コスト % はわずかに異なります。ただし、ボトルネックはまだ同じ領域にあるようです)。 実行計画

4

3 に答える 3

1

これを答えとしてマークすることに少し不安があります。クエリから少しジュースを絞り出すことができると確信しているためです. しかし、これにより、テスト実行が 22 秒から 6 秒に短縮されました (スキャンのインデックスが追加されました: DateTime と ItemID を含む OperationID):

WITH CTE AS 
(
    SELECT
        Items.ItemID AS ID          
        , Scans.DateTime AS [DateTime]
        , Operations.Cutoff AS Cutoff           
        , ROW_NUMBER() OVER (PARTITION BY Items.ID ORDER BY Scans.DateTime) AS RN
        FROM
            Items
            INNER JOIN Scans            
                ON Items.ID = Scans.ItemID
            INNER JOIN Operations
                ON Scans.OperationID = Operations.ID
            INNER JOIN Packages
                ON Items.PackageID = Packages.ID
        WHERE
            Operations.Cutoff IS NOT NULL
            AND Packages.JobID = @ID                        
)
SELECT
    Departments.LocationID
    , CTE.DateTime
    , COUNT(Items.ID) AS COUNT
FROM
    Items           
    INNER JOIN CTE
        ON Items.ID = CTE.ID
        AND CTE.RN = 1
    INNER JOIN Packages
        ON Items.PackageID = Packages.ID
    INNER JOIN Departments
        ON Items.DepartmentID = Departments.ID      
WHERE
    Packages.JobID = @ID
GROUP BY
    Departments.LocationID 
    , CTE.DateTime
于 2012-06-12T22:52:30.023 に答える
0

興味があります-CROSSAPPLYバージョンを実行していただけませんか?

SELECT
    Departments.LocationID
    , DATEADD(dd, 0, DATEDIFF(dd, 0, CA_Scans.DateTime))
    , COUNT(CA_Scans.ItemID) AS [COUNT]
FROM
    Items 
    CROSS APPLY
    (
        SELECT TOP 1
            Scans.ID,
            Scans.OperationID,
            Scans.DateTime
        FROM
            Scans
        INNER JOIN Operations
            ON Scans.OperationID = Operations.ID
        WHERE
            Operations.Cutoff IS NOT NULL
            AND Scans.ItemID = Items.ID             
        ORDER BY
            Scans.DateTime
    ) CA_Scans
    INNER JOIN Operations
        ON CA_Scans.OperationID = Operations.ID
    INNER JOIN Packages
        ON Items.PackageID = Packages.ID
    INNER JOIN Departments
        ON Items.DepartmentID = Departments.ID      
WHERE
    Packages.JobID = @ID        
GROUP BY
    Departments.LocationID 
    , DATEADD(dd, 0, DATEDIFF(dd, 0, CA_Scans.DateTime));
于 2012-06-12T23:23:47.223 に答える
0

確かに言うのは難しいですが、このような方がうまくいくかもしれません。ネストされたルックアップをROW_NUMBER呼び出しに置き換えました。元のクエリの問題は、ネストされたルックアップであり、あなたを殺します。

目の前にSQLがないのでテストできませんが、論理的には同等だと思います。

SELECT
    Departments.LocationID
    , DATEADD(dd, 0, DATEDIFF(dd, 0, Scans.DateTime))
    , COUNT(Scans.ItemID) AS [COUNT]
FROM
    Items           
    INNER JOIN Scans
        ON Scans.ItemID = Items.ID
    INNER JOIN Operations
        ON Scans.OperationID = Operations.ID
    INNER JOIN Packages
        ON Items.PackageID = Packages.ID
    INNER JOIN Departments
        ON Items.DepartmentID = Departments.ID      
WHERE
    Operations.Cutoff IS NOT NULL
    AND
    Packages.JobID = @ID
    AND
    ROW_NUMBER () OVER (PARTITION BY Items.ID ORDER BY Scans.DateTime) = 1
GROUP BY
    Departments.LocationID 
    , DATEADD(dd, 0, DATEDIFF(dd, 0, Scans.DateTime));
于 2012-06-12T20:15:15.753 に答える