sql >> データベース >  >> RDS >> Sqlserver

SQL Serverは、集計でランダムな(または最初の)値を選択します

    があります文書化されていない集計 ANYと呼ばれます これは有効な構文ではありませんが、実行プランに表示される可能性があります。ただし、これによってパフォーマンスが向上することはありません。

    次のテーブルとインデックス構造を想定しています

    CREATE TABLE T
    (
    id int identity primary key,
    [group] char(1) 
    )
    
    CREATE NONCLUSTERED INDEX ix ON T([group])
    
    INSERT INTO T
    SELECT TOP 1000000 CHAR( 65 + ROW_NUMBER() OVER (ORDER BY @@SPID) % 3)
    FROM sys.all_objects o1, sys.all_objects o2, sys.all_objects o3
    

    また、グループごとに多くの行があるように、サンプルデータを入力しました。

    元のクエリ

    SELECT MAX(id),
           [group]
    FROM   T
    GROUP  BY [group]  
    

    Table 'T'. Scan count 1, logical reads 1367 と計画

      |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([Expr1003]=MAX([[T].[id])))
           |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
    

    ANYを取得するように書き直されました 集計...

    ;WITH cte AS
    (
    SELECT *,
            ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [group] ) AS RN
    FROM T)
    SELECT id,
           [group]
    FROM    cte     
    WHERE RN=1
    

    Table 'T'. Scan count 1, logical reads 1367 と計画

      |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([[T].[id]=ANY([[T].[id])))
           |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
    

    SQL Serverは、最初の値が見つかるとすぐにグループの処理を停止し、次の値にスキップする可能性がありますが、そうではありません。それでもすべての行を処理し、論理読み取りは同じです。

    グループ内に多くの行があるこの特定の例では、より効率的なバージョンは再帰CTEです。

    WITH    RecursiveCTE
    AS      (
            SELECT TOP 1 id, [group]
            FROM T
            ORDER BY [group]
            UNION   ALL
            SELECT  R.id, R.[group]
            FROM    (
                    SELECT  T.*,
                            rn = ROW_NUMBER() OVER (ORDER BY (SELECT 0))
                    FROM    T
                    JOIN    RecursiveCTE R
                            ON  R.[group] < T.[group]
                    ) R
            WHERE   R.rn = 1
            )
    SELECT  *
    FROM    RecursiveCTE
    OPTION  (MAXRECURSION 0);
    

    Table 'Worktable'. Scan count 2, logical reads 19
    Table 'T'. Scan count 4, logical reads 12
    

    論理的な読み取りは、グループごとに最初の行を取得してから、最終結果に寄与しない大量のレコードを読み取るのではなく、次のグループを探すため、はるかに少なくなります。




    1. postgresですべてのレコードのすべての整数配列を単一の配列にマージする方法

    2. SQL Server 2008 の一時テーブルに対する nolock

    3. SQL構文の問題やコードの問題?インデックスが配列の範囲外だった。

    4. 指定された時間の5分ごとのデータの平均