web-dev-qa-db-fra.com

Supprimer la requête prend pour toujours

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é:

enter image description here

plan d'exécution réel (pour top 10 prend 28 secondes à compléter):

enter image description here

Plan d'exécution: https://www.brentozar.com/pastetheplan/?id=s1juxdruz

Statistiques client:

enter image description here

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).
  • J'ai reconstruit tous les index (en utilisant this ).
  • Essayé de supprimer de ErrorReportOrigins d'abord (en utilisant le même CTE). Aucune différence
  • (le CTE original avait un ORDER 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):

enter image description here

2
jgauffin

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.

10
Paul White 9

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.

0
paparazzo

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
0
KumarHarsh