15

約 4,500 万レコードのテーブルを持つ大規模な SQL Server データベースがあります。このテーブルをアーカイブしていますが、2 年以上前のすべてのエントリを削除する必要があります。アーカイブ テーブルへの挿入は正常に機能していますが、削除時の効率に問題があります。

私の問題は、現在テーブルにあるインデックスにあります。1000 レコード チャンクで削除 (およびアーカイブの挿入) したいと思います。これを行うには、要件を満たす "上位" 1000 件のレコード (2 年以上前) を特定する必要があります。行の DateTime スタンプはクラスター化されたインデックスであるため、これは行を取得するのに最適です。ただし、SQL 2000 では DELETE TOP 1000 が許可されていないため、次のようにする必要があります。

DELETE FROM <table> WHERE [UniqueID] IN 
(SELECT TOP 1000 [UniqueID] FROM <table> WHERE [DateTime] < @TwoYearsAgo)

UniqueID がインデックス化されている場合、これはうまく機能します。そうでないため、これには非常に長い時間がかかります (削除される 1000 レコードごとにテーブルをスキャンしています)。テーブルには、レコードを一意に識別する他のインデックスはありません。これはライブ DB であるため、UniqueID でインデックスを計算するにはコストがかかりすぎると言われています。このクエリを最適化する方法を誰か指摘できますか?

4

7 に答える 7

19

クエリを書き直してはどうですか?

SET ROWCOUNT 1000
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo

SET ROWCOUNT (Transact-SQL) に関するドキュメントを参照してください。

また、 DELETEのドキュメントによると、それは句をサポートしていますTOPが、これは明らかに SQL Server 2005 以降では新しいことです。お使いのデータベース サーバーではサポートされていないように聞こえるので、そう言っていますが、実際に使用してみましたか? SQL Server 2000 のドキュメントにアクセスできないため、そのバージョンでサポートされているかどうかわかりません。そうではないかもしれません。

DELETE TOP (1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo

TOP on select括弧なしで書く方法との違いに注意してください。UPDATE、DELETE、および INSERT の場合、上記のように定数のみの場合でも、式を括弧で囲む必要があります。

于 2009-12-17T23:09:31.113 に答える
8

サブクエリは削除できます。

DELETE <table> FROM (
  SELECT TOP 1000 *  
  FROM <table>
  WHERE [DateTime] < @TwoYearsAgo);

例E:SQL2000DELETE構文を参照してください。これは、SETROWCOUNTアプローチよりも推奨されます。SQL 2005以降では、DELETEでTOPを直接指定できます。

于 2009-12-18T00:04:47.160 に答える
4

あなたもできる

DELETE TOP(1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo

なぜ削除に top(x) を使用し、選択に top x を使用するのかは神のみぞ知るところですが、ほとんどの人はこの機能についても知らないようです!

編集:どうやら2005年以降なので、おそらくこれを無視する必要があります。

于 2009-12-17T23:21:57.777 に答える
2

私はしばらく前に似たようなことをしなければなりませんでした - 古いレコードをアーカイブテーブルに移動するために軽量の挿入と削除を行います。直感に反しますが、私が見つけた最速かつ最も影響の少ない解決策は次のとおりです。

  1. 上位 (x) 行の ID の値を含む小さな #temp テーブルを作成します。シナリオで ID を実際にインデックス化できない場合は、代わりに日付と ID を使用して、2 つの組み合わせでインデックスを使用できます。

  2. トランを開始

  3. ID と DATE が ( #temp ) のアーカイブ テーブルに挿入します。

  4. ( #temp ) に ID と DATE があるメイン テーブルから削除します。

  5. 専念

  6. #temp を切り捨てる

  7. 繰り返す

行識別子をステージングするための一時テーブルを持つことは、単純な削除よりも総作業量が多くなりますが、ブロックせずに一度に少しずつ削除したい場合には、プロセスが非常に軽量になります。

また、私はLasseに同意します-インデックスのない一意のIDのポイントを見ることができないため、それを強制するための制約がありません。

于 2009-12-17T23:28:41.403 に答える
2

SET ROWCOUNTを使用できます。

SET ROWCOUNT 1000
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo
于 2009-12-17T23:13:42.930 に答える
0

1000 レコード チャンクの要件に固執する必要があるかどうか疑問に思います。サーバーの負荷と任意の種類の理由でそこにある場合は、[DateTime] にクラスター化インデックスが既にあるため、次のことを試してください。

DELETE FROM <table> 
WHERE [DateTime] < @TwoYearsAgo 
and [DateTime] < (select dateadd(day, 1, min([DateTime])) from <table>)
于 2009-12-18T17:36:47.987 に答える