it-swarm-eu.dev

Metody urychlení obrovského DELETE FROM <table> bez klauzulí

Používání serveru SQL Server 2005.

Provádím obrovský ODSTRANIT OD bez klauzulí. Je to v zásadě ekvivalentní příkazu TRUNCATE TABLE - kromě toho, že nemůžu používat TRUNCATE. Problém je v tom, že tabulka je obrovská - 10 milionů řádků a dokončení trvá přes hodinu. Existuje nějaký způsob, jak zrychlit bez:

  • Použití zkrátit
  • Zakázání nebo zrušení indexů?

T-log je již na samostatném disku.

Jakékoli návrhy vítány!

37
tuseau

To, co můžete udělat, je hromadné mazání takto:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable

Kde xxx je, řekněme, 50000

Úprava tohoto, pokud chcete odstranit velmi vysoké procento řádků ...

SELECT col1, col2, ... INTO #Holdingtable
           FROM MyTable WHERE ..some condition..

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable WHERE ...

INSERT MyTable (col1, col2, ...)
           SELECT col1, col2, ... FROM #Holdingtable
39
gbn

Dalo by se použít klauzuli TOP, abyste to snadno provedli:

WHILE (1=1)
BEGIN
    DELETE TOP(1000) FROM table
    IF @@ROWCOUNT < 1 BREAK
END
21
SQLRockstar

Souhlasím s návrhy na rozdělení vašich deletů do zvládnutelných bloků, pokud nejste schopni používat TRUNCATE, a líbí se mi návrh na přetažení/vytvoření pro jeho originalitu, ale jsem zvědavý na následující komentář ve vaší otázce:

Je to v podstatě ekvivalentní příkazu TRUNCATE TABLE - kromě toho, že nemám dovoleno používat TRUNCATE

Hádám, že důvod tohoto omezení souvisí s bezpečností, která musí být udělena pro přímé zkrácení tabulky, a se skutečností, že by vám to umožnilo zkrátit jiné tabulky než ty, kterých se týkáte.

Za předpokladu, že tomu tak je, jsem zvědavý, zda by mít vytvořenou uloženou proceduru, která používá TRUNCATE TABLE a používá "EXECUTE AS", bylo považováno za schůdnou alternativu k udělení bezpečnostních práv nezbytných k přímému zkrácení tabulky.

Doufejme, že vám to poskytne rychlost, kterou potřebujete, a zároveň vyřešíte bezpečnostní obavy, které může mít vaše společnost při přidávání účtu do role db_ddladmin.

Další výhodou použití uložené procedury tímto způsobem je, že samotná uložená procedura může být uzamčena, takže ji mohou používat pouze konkrétní účty.

Pokud to z nějakého důvodu není přijatelné řešení a vaše potřeba mít data v této tabulce odstraněna je něco, co je třeba udělat jednou denně/hodinu/atd., Chtěl bych požádat o vytvoření úlohy SQL Agent pro zkrácení tabulky každý den v plánovaném čase.

Snad to pomůže!

7
Jeff

Kromě zkrácení .. vám může pomoci pouze smazání v dávkách.

Mohli byste zrušit tabulku a znovu ji vytvořit, se všemi omezeními a indexy, samozřejmě. V Management Studio máte možnost skriptovat tabulku, kterou chcete přetáhnout a vytvořit, takže by měla být triviální možností. Ale to jen pokud máte povoleno provádět akce DDL, což je podle mého názoru opravdu nemožné.

5
Marian

Vzhledem k tomu, že tato otázka je tak důležitým odkazem, odesílám tento kód, který mi opravdu pomohl porozumět vymazání pomocí smyček a také zasílání zpráv v rámci smyčky ke sledování pokroku.

Dotaz je upraven z tato duplicitní otázka. Kredit za @ RLF za základnu dotazu.

CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects;  -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME 
DECLARE @currenttime DATETIME 

SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0  -- Total rows deleted so far is 0
SET @HowMany = 5;     -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()

WHILE @RowsTouched > 0
BEGIN
   DELETE TOP (@HowMany)
   FROM #DelTest 
   WHERE name LIKE '%sys%';

   SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
   SET @TotalRowCount = @[email protected]; -- Increment Total rows deleted count
   SET @currenttime = GETDATE();
   SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
   RAISERROR(@msg, 0, 1) WITH NOWAIT;  -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.  

END; 
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
1
Max xaM