6

データをプルしてサービスにキャッシュする動的SQLクエリを作成するデータロードシナリオがあります。すべての製品データを含む1つのテーブルがあります:ProductHistory(47列、200,000レコード+そして今後も増え続ける)

必要なもの: 最大ID、最大バージョン、最大変更IDを使用して最新の製品を入手してください。

最初の試み:

SELECT distinct Product.* FROM ProductHistory product 
WHERE  product.version = 
(SELECT max(version) from ProductHistory p2 where product.Id = p2.Id 
  and product.changeId = 
(SELECT max(changeid) from ProductHistory p3 where p2.changeId = p3.changeId))

これには2.51分以上かかりました。

その他の失敗した試み:

select distinct product.* from ProductHistory product 
where CAST(CAST(id as nvarchar)+'0'+CAST(Version as nvarchar)+'0'+CAST(changeid as nvarchar) as decimal) = 
(select MAX(CAST(CAST(id as nvarchar)+'0'+CAST(Version as nvarchar)+'0'+CAST(changeid as nvarchar) as decimal)) from ProductHistory p2 
where product.Id = p2.Id)

基本的に、日付を注文するときと同じ原則を使用し、関連性の高い順に番号を連結します。

For example 11 Jun 2007 = 20070711
And in our case: Id = 4 , version = 127, changeid = 32   => 40127032
The zeros are there not to mix up the 3 different ids

しかし、これは3.10分かかります!!! :(

したがって、基本的に、最初の試行クエリを万が一改善する方法が必要です。また、このような量のデータについても疑問に思っていましたが、これは私が期待する最高の検索速度ですか?

  1. sp_helpindex ProductHistoryを実行し、以下のようにインデックスを見つけました。

    PK_ProductHistoryNew-PRIMARY- Id、バージョンにあるクラスター化された一意の主キー

  2. 最初のクエリをSPでラップしましたが、それでも変更はありません。

では、他にどのような方法でこの操作のパフォーマンスを向上させることができるのでしょうか。

ありがとう、Mani ps:時間を確認するためにSQLManagementStuidoでこれらのクエリを実行しています。

4

8 に答える 8

6

Sql Server Management Studio からクエリを実行し、クエリ プランを見てボトルネックがどこにあるかを確認します。「テーブルスキャン」または「インデックススキャン」が表示される場所では、探しているものを見つけるためにすべてのデータを調べる必要があります。これらの操作に使用できる適切なインデックスを作成すると、パフォーマンスが向上するはずです。

于 2011-07-20T10:02:46.790 に答える
4

私が見るいくつかのこと:

  • DISTINCT必要ですか?を行う場合DISTINCT *、メリットはほとんどありませんが、すべてのフィールドで重複をチェックするためのオーバーヘッドが発生します。
  • WHERE句に2 つのサブセレクトを含める代わりにJOIN、派生テーブルに。これは一度だけ処理する必要があります。あなたのWHERE条項が複数回処理されていると思われます。

<-- -->

SELECT Product.* 
FROM ProductHistory product 
INNER JOIN ( SELECT P.Id, 
                    MAX(p.version) as [MaxVer], 
                    MAX(p.Changeid) as [MaxChange]
             FROM Product p
             GROUP BY p.ID) SubQ
    ON SubQ.ID = product.ID
    AND SubQ.MaxChange = Product.ChangeID
    AND SubQ.MaxVer = Product.Version

このためのインデックスも必要Id, Version, ChangeIDです。

于 2011-07-20T10:19:30.000 に答える
1

まあ、すべてをテーブルに格納するのは適切ではありません。最後のバージョンをテーブルに保存し、履歴に別のバージョン (同じ構造を持つ) を使用することをお勧めします (古い製品よりも現在の製品に関心があると思います)。また、コンセプトの問題により、多くの回避策が作成されます...

また、クエリの問題を隠すことが多いため、DISTINCT は使用しないでください (通常、重複が取得された場合は、より適切に最適化できることを意味します)。

さて、最良の部分: 問題を解決する方法は? 次のようなグループ化の原則を使用する必要があると思います。

SELECT max(id), max(version), max(changeid) 
  FROM ProductHistory p
  WHERE <filter if necessary for old products or anything else>
  GROUP BY version, changeid
  HAVING version = max(version)
     AND changeid = max(changeid)
     AND id = max(id)

しかし、私があなたの PK を見ると、驚いたことに、id とバージョンのみを処理する必要があるため、changeid は関係ありません...

私はテストできないため、私の要求が完全に正しいかどうかはわかりませんが、いくつかのテストを行うことはできると思います。

于 2011-07-20T10:16:25.223 に答える
0

(Id, changeId, version)このクエリにはインデックスが必要だと思います。テーブル定義、現在のテーブルのインデックス、およびクエリのクエリ プランを提供してください。

于 2011-07-20T10:20:32.463 に答える
0

行数が増えると、このクエリの時間が長くなる気がしますが、試してみる価値はあります。

SELECT * FROM 
(
SELECT Col1, Col2, Col3,
ROW_NUMBER() OVER (PARTITION BY ProductHistory.Id ORDER BY Version DESC, ChangeID DESC) AS RowNumber 
FROM ProductHistory
)
WHERE RowNumber = 1
于 2011-07-20T12:45:34.807 に答える
0

この CTE を試してみてください。これは可能な限り最速のオプションであり、高速化するためにインデックスさえ必要ないでしょう。

with mysuperfastcte as (
 select product.*, 
 row_number() over (partition by id order by version desc) as versionorder,
 row_number() over (partition by id order by changeid desc) as changeorder 
 from ProductHistory as product
)
select distinct product.*
from mysuperfastcte
where versionorder = 1
and changeorder = 1;

注意。コードのこの時点でバグがある可能性があると思いますので、私のコードで期待する結果を確認して再確認してください。

  and product.changeId =  (SELECT max(changeid) from ProductHistory p3 where p2.changeId = p3.changeId))
  • 相関サブクエリを使用して max(changeid) を取得しようとしていますが、changeid にも参加しています。これは、すべての行を取得することと同じです。もしかして、そんなつもりじゃなかった?

また、明らかに、返す列の数を必要なものだけに減らしてから、クエリを実行する前に次を実行し、メッセージ出力を確認します。

統計IOをオンに設定

論理読み取りが多いテーブルを探し、インデックスが役立つ場所を見つけます。

ヒント: 私のコードがうまくいく場合は、必要な列に応じて次のことができます。

ProductHistory にインデックス ix1 (id, version desc) include (changeid, .... ) を作成します。

これが役立つことを願っています!

于 2012-09-04T02:40:55.897 に答える
0

これは少しおかしくなっていますが、パーティショニングが機能するかどうか疑問に思っています:

  SELECT Id
  FROM (
      SELECT Id,
      MAX(version) OVER (PARTITION BY changeId) max_version
      FROM ProductHistory
  ) s
  where version = s.max_version
于 2011-07-20T11:21:42.453 に答える
-2

一般的に言えば、 select max() はテーブル全体をソートする必要があります。そして、あなたはそれを2回やっています

SELECT TOP 1 の方がはるかに高速ですが、インデックスが正しく、ORDER BY が正しいことを確認する必要があります。あなたがそれで遊ぶことができるかどうか見てください。

于 2011-07-20T10:15:40.497 に答える