web-dev-qa-db-fra.com

La suppression par lots de SQL Server à l'aide de la boucle WHILE ne fonctionne pas

J'ai une très grande table, j'utilise donc les éléments suivants pour supprimer les entrées plus anciennes:

WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103
END

Je l'ai couru plusieurs fois en utilisant des dates différentes. Parfois, cela fonctionne bien (prend environ 20 minutes), mais parfois, la requête se termine immédiatement et rien n'a été supprimé. Lorsque cela se produit, je fais simplement une instruction SELECT simple à partir de cette table, puis réessaie l'instruction WHILE ci-dessus, puis tout fonctionne! Quelqu'un sait pourquoi c'est? J'ai besoin d'automatiser cette requête pour qu'elle s'exécute régulièrement afin de contrôler la taille de la table, mais je veux m'assurer qu'elle est correctement supprimée lors de son exécution. Je vous remercie.

11
JCB

Vraisemblablement, la raison est parce que @@ROWCOUNT est initialisé à une valeur de 0.

Vous pouvez d'abord lancer cette requête pour la définir:

select count(*) from myTable where date < 20130103

Cela ajouterait un peu de temps à votre requête, mais vous verriez le nombre de lignes supprimées.

Vous pouvez aussi faire quelque chose comme:

select top 1     * from myTable 

ce qui irait beaucoup plus vite.

9
Gordon Linoff

Qu'est-ce que vous exécutez avant ce bloc de code? @@ROWCOUNT sera défini sur l’instruction qui l’a précédé. Si vous exécutez une autre commande au préalable, il pourrait s'agir de 0.

Au lieu de cela, vous pouvez forcer le nombre initial à être 1:

DECLARE @Rows INT
SET @Rows = 1

WHILE (@Rows > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103

    SET @Rows = @@ROWCOUNT
END
29

C'est parce que parfois @@ROWCOUNT est zéro pour commencer - ainsi la boucle while ne s'exécute jamais, car elle vérifie la condition before chaque exécution, y compris la première.

Voici une boucle do-while faite maison, car SQL Server n’en a pas.

loop:
   DELETE TOP (5000) FROM myTable
   WHERE date < 20130103
if @@ROWCOUNT > 0 goto loop
5
Blorgbeard

Vous pouvez également écrire votre requête de cette façon: 

SET ROWCOUNT 5000; -- set batch size
WHILE EXISTS (SELECT 1 FROM myTable WHERE date < '2013-01-03')
BEGIN
    DELETE FROM myTable
    WHERE date < '2013-01-03'
END;
SET ROWCOUNT 0; -- set batch size back to "no limit"

Dans les deux cas, vous devez formater correctement vos chaînes de date.

Assurez-vous simplement que vos critères de suppression et l’énoncé de votre clause exist sont identiques ou vous risquez de rencontrer une boucle infinie.

1
Jim Clouse

Lorsque je supprime en lot, j'ajoute un WAITFOR DELAY (au moins 1 ou 2 secondes). Aussi la première déclaration que j'ai créée en dehors de la boucle. Par ailleurs, évitez d'utiliser ROWCOUNT comme délimiteur ( https://docs.Microsoft.com/en-us/sql/t-sql/statements/set-rowcount-transact-sql?view=sql-server-2017 ) . Il y a deux options:

DECLARE @BatchSize BIGINT = 50000
SET ROWCOUNT @BatchSize

DELETE 
FROM myTable 
WHERE 
date < 20130103

WHILE (@@ROWCOUNT > 0)
BEGIN
WAITFOR DELAY '00:00:02'
DELETE 
FROM myTable 
WHERE 
date < 20130103
END

Ou 

DECLARE @BatchSize BIGINT = 50000

WHILE 1=1
BEGIN

    WAITFOR DELAY '00:00:02'

    DELETE TOP(@BatchSize )
    FROM myTable 
    WHERE 
    date < 20130103

    IF @@ROWCOUNT < @BatchSize 
        Break
END
0
Eduardo Gelvis

Fondamentalement,

SELECT 0 -- rowcount is 1 
WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103
END

Ou

SET ROWCOUNT 5000 -- set row count to 5000
SELECT 0 -- rowcount is 1 
WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE FROM myTable
    WHERE date < 20130103
END
SET ROWCOUNT 0  -- set rowcount to unlimited
0
Ali Osman Yavuz