1

次の構造を持つストアプロシージャがあります

WITH ItemsContact (
    IsCostVariantItem
    ,ItemID
    ,AttributeSetID
    ,ItemTypeID
    ,HidePrice
    ,HideInRSSFeed
    ,HideToAnonymous
    ,IsOutOfStock
    ,AddedOn
    ,BaseImage
    ,AlternateText
    ,SKU
    ,[Name]
    ,DownloadableID
    ,[Description]
    ,ShortDescription
    ,[Weight]
    ,Quantity
    ,Price
    ,ListPrice
    ,IsFeatured
    ,IsSpecial
    ,ViewCount
    ,SoldItem
    ,TotalDiscount
    ,RatedValue
    ,RowNumber
    )
AS (
    SELECT ------,
        ROW_NUMBER() OVER (
            ORDER BY i.[ItemID] DESC
            ) AS RowNumber
    FROM -------
    )
    ,rowTotal (RowTotal)
AS (
    SELECT MAX(RowNumber)
    FROM ItemsContact
    )
SELECT CONVERT(INT, r.RowTotal) AS RowTotal
    ,c.*
FROM ItemsContact c
    ,rowTotal r
WHERE RowNumber >= @offset
    AND RowNumber <= (@offset + @limit - 1)
ORDER BY ItemID

これを実行すると、実行計画から見つけました

SQL Server Execution Times:
   CPU time = 344 ms,  elapsed time = 362 ms.

今、私は2番目のcte、すなわちrowTotalを削除します

WITH ItemsContact (
    IsCostVariantItem
    ,ItemID
    ,AttributeSetID
    ,ItemTypeID
    ,HidePrice
    ,HideInRSSFeed
    ,HideToAnonymous
    ,IsOutOfStock
    ,AddedOn
    ,BaseImage
    ,AlternateText
    ,SKU
    ,[Name]
    ,DownloadableID
    ,[Description]
    ,ShortDescription
    ,[Weight]
    ,Quantity
    ,Price
    ,ListPrice
    ,IsFeatured
    ,IsSpecial
    ,ViewCount
    ,SoldItem
    ,TotalDiscount
    ,RatedValue
    ,RowNumber
    )
AS (
    SELECT ------,
        ROW_NUMBER() OVER (
            ORDER BY i.[ItemID] DESC
            ) AS RowNumber
    FROM -------
    )
SELECT c.*
FROM ItemsContact c
    ,rowTotal r
WHERE RowNumber >= @offset
    AND RowNumber <= (@offset + @limit - 1)
ORDER BY ItemID

そして、実行計画を次のように表示します

SQL Server Execution Times:
   CPU time = 63 ms,  elapsed time = 61 ms.

rowtotal を計算する最初のコードは問題なく動作しますが、時間がかかります。私の質問は、なぜそんなに時間がかかるのMAX(RowNumber)か、このコードを最適化するにはどうすればよいのかということです。

4

2 に答える 2

1

コーディングした方法では、SQL に構文エラーがあります。rowTotalもう 1 つのことは、2 番目のクエリから削除した場合、それへの参照がまだ残っているため、単純に機能しないということです。したがって、これらの 2 回目の実行時間がどこから来たのかはわかりません。

ただし、コード ブロックをテンプレートとして使用してエラーを削除すると、このクエリの実行計画は非常に単純になります。テーブルに (クラスター化された)インデックス スキャンを実行し、演算子を並べ替え、その他の演算子 (ランキング ROW_NUMBER 関数、ネストされたループなどの結合演算子など)。クラスタ化されたインデックスのスキャンと並べ替えは、最もプロセッサを集中的に使用する操作である必要があります。-------

ここで、SQL サーバーは各行の行番号を計算し、その最大値と、入力変数から計算された 2 つの行番号の間の制約結果を見つける必要があります。明らかに、このクエリを使用して構築されたページング機能があり、SO 上の SQL Server にはページングに関する多くの情報があるため、検索すると多くの関連情報を見つけることができます。

このクエリに基づいて構築された既知のレイヤーがある場合は、それを変更する必要があります。max(row_number(ID))すべての行(38k?)で一定であり、論理的にはスカラー値だけを持っているため、追加の不必要な列を使用します。代わりにcount(*)、@Damien_The_Unbeliever がそのソリューションで提案したように返す必要がありますが、結果セットから分離してください。このようにして、クエリを簡素化し、代わりに次のようにします。

SELECT
  N,
  *
FROM 
  YourTable 
  CROSS apply(
    SELECT N = ROW_NUMBER() OVER (ORDER BY ItemID DESC)
  ) x
WHERE N BETWEEN @offset AND @offset + @limit - 1
ORDER BY ItemID

次のクエリで結果カウントを取得するのは簡単なはずです。また、非常に大きなテーブルがある場合は、このメソッドを使用しておおよその行数を数えることができます。

PS インデックスの問題について実行計画をまだチェックしていない場合は、チェックしてください。

于 2013-07-17T09:38:59.087 に答える