web-dev-qa-db-ja.com

更新ステートメントは完了するまでに時間がかかります

1250000レコード前後のテーブルと80列があります

テーブルの構造

CREATE TABLE [dbo].[Table] (
[Column1] [int] IDENTITY(1, 1) NOT FOR REPLICATION NOT NULL, [Column2] [int] NULL, [Column3] [int] NULL, 
[Column4] [int] NULL, [Column5] [int] NULL, [Column6] [int] NULL, [Column7] [int] NULL, [Column8] [int] NULL, 
[Column9] [int] NULL, [Column10] [varchar](20) NULL, [Column11] [datetime] NULL, [Column12] [int] NULL, 
[Column13] [money] NULL, [Column14] [money] NULL, [Column15] [varchar](40) NULL, [Column16] [int] NULL, 
[Column17] [int] NULL, [Column18] [varchar](20) NULL, [Column19] [varchar](50) NULL, [Column20] [int] NULL, 
[Column21] [int] NULL, [Column22] [int] NULL, [Column23] [int] NULL, [Column24] [int] NULL, 
[Column25Date] [datetime] NULL, [Column26] [int] NULL, [Column27] [int] NULL, [Column28] [int] NULL, 
[Column29] [int] NULL, [Column30] [int] NULL, [Column75] [varchar](50) NULL, [Column31] [int] NULL, 
[Column32] [varchar](15) NULL, [Column33] [int] NULL, [Column34] [money] NULL, 
[Column35] [dbo].[T_Boolean2] NOT NULL, [Column36] [money] NULL, [Column37] [money] NULL, 
[Column38] [int] NULL, [Column39] [int] NULL, [Column40] [varchar](255) NULL, [Column41PrintDate] [datetime] NULL, 
[Column42PostDate] [datetime] NULL, [Column43FirstName] [varchar](30) NULL, [Column44LastName] [varchar](30) NULL, 
[Column45Email] [varchar](50) NULL, [Column46PrimaryPhone] [varchar](10) NULL, 
[Column47SecondaryPhone] [varchar](10) NULL, [Column48PrimaryPhoneType] [smallint] NULL, 
[Column49SecondaryPhoneType] [smallint] NULL, [Column50PrimaryPhoneType] [smallint] NULL, 
[Column51SecondaryPhoneType] [smallint] NULL, [Column52EndDate] [datetime] NULL, 
[Column53] [dbo].[T_Boolean2] NOT NULL, [Column54] [int] NULL, [InsertTimeStamp] [datetime] NOT NULL, 
[UpdateTimeStamp] [datetime] NULL, [Column55ID] [int] NULL, [Column56] [int] NULL, [Column57] [int] NULL, 
[Column58] [int] NULL, [Column59] [money] NULL, [Column60] [money] NULL, [Column61] [money] NOT NULL, 
[Column62] [money] NOT NULL, [Column63] [varchar](30) NULL, [Column64] [varchar](30) NULL, 
[Column65] [varchar](10) NULL, [Column66] [varchar](6) NULL, [Column67] [varchar](10) NULL, 
[Column68] [varchar](35) NULL, [Column69Date] [datetime] NULL, [Column70Date] [datetime] NULL, 
[Column71Date] [datetime] NULL, [Column72] [varchar](255) NULL, [Column73PIN] [varchar](6) NULL, 
[Column74Date] [datetime] NULL, [Column76Date] [datetime] NULL, 
CONSTRAINT [XPKColumn1] PRIMARY KEY CLUSTERED (
[Column1] ASC
 ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

2つの更新トリガーがあり、バックグラウンド計算を実行して、ステータスが変化したときやその他の条件が発生したときに同じテーブルを更新します

私が更新すると、1時間以上実行されているが、レコードを更新できなかった通常の場所の列が1つあるため、ステートメントを強制終了します

Update Table Column5 = 12345 where Column5 = 6789

約35000レコードを更新する必要があります

テーブルにはこの列の適切なインデックスがあり、手動でテーブルの統計を更新していますが、更新に時間がかかりすぎます

テーブルには約10のインデックスがあります

それから私はそれが更新された2つのバッチで更新しようとしましたが、非常に時間がかかりました

同じテーブルの他の列を更新しようとしましたが、更新に数秒かかりました

Update Table Column3 = 6789 where Column3 = 12345

更新ステートメントのクエリ速度を向上させ、上記の単純なロジックを使用する方法

解決策を提案してください、

前もって感謝します

6
sairam

トリガーのコードを確認せずに原因を正確に説明することは困難ですが、INSERTEDおよびDELETED仮想テーブルの誤用が原因であると考えられる、ゼロ以外の量はあると思います。

INSERTEDおよびDELETEDについてのことは、インデックスがないことです。それらを直接ベーステーブル(または相互に)に結合すると、非常にひどいパフォーマンスになる可能性があります。

通常、2つを一時テーブルにロードし、それらを結合する(通常は主キーで)ためのインデックスを作成することで、これを軽減できます。

SELECT *
INTO #i
FROM INSERTED

CREATE CLUSTERED INDEX CX_i ON #i (primary_key)

SELECT *
INTO #d
FROM DELETED

CREATE CLUSTERED INDEX CX_d ON #d (primary_key)

とにかく、そのようなもの。次に、一時テーブルのみを使用し、INSERTEDおよびDELETEDには触れないでください。

一時テーブル名はセッションごとに一意である必要があることに注意してください。カスケードする複数のトリガーがある場合、一意のテーブル名を使用する必要があります。これが問題になる場合は、代わりにテーブル変数を使用することをお勧めします。これらの変数は、実行中のトリガー/プロシージャにスコープが設定されており、名前の衝突のリスクがありません。 (ただし、テーブル変数にインデックスを付ける唯一の方法は、DECLAREステートメントでクラスター化された主キーを宣言することです。)

5
db2

小さなグループでテーブルを更新することにより、更新操作のパフォーマンスを向上させることができます。

例を参照:

SET rowcount 10000

    Update Table 
      Column3 = 6789 
      where Column3 = 12345

    while @@rowcount>0

    Update Table 
      Column3 = 6789 
      where Column3 = 12345

    END

    SET rowcount 0

上記のコードは一度に10000行を更新し、@@ rowcountの値がゼロになるまでループが続きます。これにより、テーブルがロックされなくなります。

SQL Serverで大きなテーブルを更新する際のベストプラクティス

  • 更新するデータを制限するには、常に[〜#〜] where [〜#〜]句を使用します
  • テーブルにindicesが多すぎる場合は、更新中に無効にし、更新後に再度有効にすることをお勧めします
  • テーブルをシングルショットで更新するのではなく、上記の例に示すようにグループに分割します。

別の追加-そして、これは少し複雑になるかもしれませんが、大きなテーブルを特定のフィールドでパーティション化してから、それらのパーティションを複数のディスクに分散するのが良い場合です。

3
Up_One