私は合理的な単純なクエリを取得しました:
With RowsToDelete AS
(
SELECT TOP 500 Id
FROM ErrorReports
WHERE IncidentId = 611
)
DELETE FROM RowsToDelete
ただし、完了しません。何度か試してみました。最後に8分待ってからキャンセルしました。
ErrorReports
には約22,000行が含まれています。 ErrorReportOrigins
ほぼ同じ。
推定実行計画:
実際の実行計画(top 10
の場合、完了するまでに28秒かかります):
実行計画: https://www.brentozar.com/pastetheplan/?id=S1jUXDruz
クライアント統計:
私が試したこと:
ErrorReportOrigins
にはクラスター化インデックス(id)がなく、ErrorReports.Id
へのFKしかありませんでした。 id列(pk&identity)を追加しました。ErrorReportOrigins
から削除しようとしました(同じCTEを使用)。変わりはないORDER BY Id
がありましたが、差があるかどうかを確認するために削除しました)道に迷いました。一体どうしてこんなに時間がかかるの?すべてのSELECTステートメントは高速です。そして、DBはそれほど大きくありません。 ErrorReports
テーブルは最大のものです。
(これはエラスティックプール内のSQL Azure DBです)
更新
CREATE TABLE [dbo].[ErrorReports](
[Id] [int] IDENTITY(1,1) NOT NULL,
[IncidentId] [int] NOT NULL,
[ErrorId] [varchar](36) NOT NULL,
[ApplicationId] [int] NOT NULL,
[ReportHashCode] [varchar](20) NOT NULL,
[CreatedAtUtc] [datetime] NOT NULL,
[SolvedAtUtc] [datetime] NULL,
[Title] [nvarchar](100) NULL,
[RemoteAddress] [varchar](45) NULL,
[Exception] [ntext] NOT NULL,
[ContextInfo] [ntext] NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
インデックス:
CREATE NONCLUSTERED INDEX [Application_GetWeeklyStats] ON [dbo].[ErrorReports]
(
[ApplicationId] ASC,
[CreatedAtUtc] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
CREATE NONCLUSTERED INDEX [ErrorReports_IncidentId] ON [dbo].[ErrorReports]
(
[IncidentId] ASC,
[CreatedAtUtc] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
データはかなり大きい(クエリでdatalength
を使用):
非常に大きなntext
データは非常に断片化されている可能性が高いため、削除するLOBフラグメントを検索するときに大量のランダムI/O(またはその他の非効率)が発生します。たぶんエラスティックなものには、より多くのI/O馬力も必要です。
この問題を解決するには、データをエクスポートして再ロードする必要がある場合があります。新しいテーブルにコピーし、古いテーブルを削除してから、新しい名前を変更することもできます。
特別な理由がない限り、データの再読み込み中に、データ型を古い非推奨のntext
から代わりのnvarchar(max)
に変更することをお勧めします。
また、LOBが大きい場合のSQL Serverの制限である可能性もあります。関連するQ&A SQL ServerでのLOBデータのスロー削除 を参照してください。
通常の推奨事項は、データの平均が1MB以上の場合に代替ストレージソリューションに移行することです。詳細については、Paul S. RandalによるSQL Serverテクニカルペーパー SQL Server 2008のFILESTREAMストレージ を参照してください。これは まだサポートされていません 残念ながらAzure SQLデータベースではです。
代わりに、大きなLOBデータをAzure Blobストレージに格納し、データベース自体にリンクのみを保持する方がよいでしょう。
それが違いをもたらすかどうかはわかりませんが、この構文はうまくいくはずです:
DELETE TOP (500)
FROM ErrorReports
WHERE IncidentId = 611
シンプルなテーブルでテストし、単一のインデックススキャンで非常にシンプルなクエリプランを取得しました。
IMO、_[Exception] ,[ContextInfo]
_のサイズを再検討する必要があります。
たとえば、len(Exception)> 1000のテーブルに行がありますか
varchar(1500)
で十分だと思います。指定したクエリが改善されます。
サイズは変更できない場合があります。
あるいは、RBAR
に行くこともできます
_DECLARE @IncidentId INT = 611
DECLARE @TopSize INT = 10 --try 15,20 etc
DECLARE @BatchSize INT = 10 --try 15,20 etc
DECLARE @MaxLimit INT = 500
DECLARE @RowCount INT = 0
BEGIN TRY
WHILE (@TopSize <= @MaxLimit)
BEGIN
DELETE TOP (@TopSize)
FROM ErrorReports
WHERE IncidentId = @IncidentId
SET @RowCount = @@RowCount
--PRINT @TopSize
IF (
@RowCount = 0
OR @RowCount IS NULL
)
BREAK;
ELSE
SET @TopSize = @TopSize + @BatchSize
END
END TRY
BEGIN CATCH
--catch error
END CATCH
_