J'ai eu une requête simple raisonnable:
With RowsToDelete AS
(
SELECT TOP 500 Id
FROM ErrorReports
WHERE IncidentId = 611
)
DELETE FROM RowsToDelete
Cependant, cela ne complète pas. J'ai essayé plusieurs fois. La dernière fois que j'ai attendu 8 minutes avant de l'annuler.
ErrorReports
contient environ 22 000 lignes. ErrorReportOrigins
à peu près la même chose.
Plan d'exécution estimé:
plan d'exécution réel (pour top 10
prend 28 secondes à compléter):
Plan d'exécution: https://www.brentozar.com/pastetheplan/?id=s1juxdruz
Statistiques client:
Ce que j'ai essayé:
ErrorReportOrigins
n'a pas eu d'index clustered (ID), seulement un FK à ErrorReports.Id
. J'ai ajouté une colonne d'identité (PK & Identity).ErrorReportOrigins
d'abord (en utilisant le même CTE). Aucune différenceORDER BY Id
, Je l'ai supprimé pour voir s'il y avait une différence)Je suis perdu. Pourquoi sur Terre prend-il si longtemps? Toutes les déclarations SELECT sont rapides. Et le dB n'est pas vraiment grand. Le tableau ErrorReports
est le plus grand.
(Ceci est un DB SQL Azure dans une piscine élastique)
Mise à jour
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)
)
Index:
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)
Les données sont assez grandes (utilisées datalength
dans la requête):
Il semble probable que les données très grandes ntext
sont très fragmentées, entraînant une grande quantité d'E/S aléatoires (ou d'autres inefficiences) lors de la localisation de fragments de LOB à supprimer. Peut-être que la chose élastique a besoin de plus de puissance I/O.
Vous devrez peut-être exporter et recharger les données pour résoudre ce problème. Copier sur une nouvelle table, laissant tomber l'ancien, puis renommer que le nouveau fonctionnait également.
Sauf si vous avez une raison extrêmement bonne de ne pas, je vous suggère de modifier également le type de données de l'ancien, obsolète ntext
au remplacement nvarchar(max)
lors du rechargement des données.
Il peut également s'agir simplement d'être une limitation du serveur SQL avec de grands lobes. Voir le Q & A associé Delete lente des données LOB dans SQL Server .
La recommandation habituelle consiste à passer à une solution de stockage alternative lorsque les données moyennes de 1 Mo ou plus. Consultez le document technique SQL Server stockage de filtream dans SQL Server 2008 par Paul S. Randal pour plus de détails. Ceci est pas encore pris en charge dans la base de données Azure SQL, malheureusement.
Peut-être préféreriez-vous mieux stocker les grandes données LOB dans Azure Blob Stockage et ne contenant qu'un lien dans la base de données elle-même.
Je ne sais pas si cela fera une différence, mais cette syntaxe doit fonctionner:
DELETE TOP (500)
FROM ErrorReports
WHERE IncidentId = 611
J'ai testé sur une table simple et obtenez un plan de requête très simple avec une seule analyse d'index.
Imo, vous devez reconsidérer la taille de [Exception] ,[ContextInfo]
.
par exemple, y a-t-il une rangée dans la table où Len (Exception)> 1000
Je pense que varchar(1500)
suffit, cela améliorera votre requête donnée.
Peut être la taille ne peut pas modifier.
Sinon, vous pouvez aller pour 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