2010-01-26 19 views
8

Załóżmy, że mam tabelę z rekordem 10000000. Jaka jest różnica między tymi dwoma rozwiązaniami?Usuń dużą ilość danych w serwerze sql

  1. usunąć dane, takie jak:

    DELETE FROM MyTable 
    
  2. usuwanie wszystkich danych aplikacji z rzędu przez rzędu:

    DELETE FROM MyTable WHERE ID = @SelectedID 
    

to pierwsze rozwiązanie ma najlepszą wydajność? jaki jest wpływ na log i wydajność?

Odpowiedz

14

Jeśli masz, że wiele rekordów w tabeli i chcesz je usunąć wszystkie, należy rozważyć truncate <table> zamiast delete from <table>. Będzie to znacznie szybsze, ale pamiętaj, że nie może aktywować wyzwalacza.

Zobacz więcej szczegółów (w tym przypadku serwera SQL 2000): http://msdn.microsoft.com/en-us/library/aa260621%28SQL.80%29.aspx

Usuwanie tabeli w aplikacji wiersz po wierszu skończy się w długi, długi czas, ponieważ DBMS nie może nic optymalizacji, jak nie wie z góry, że wszystko usuniesz.

+4

Możesz wycofać skasowanie, jeśli nadal jesteś w zakresie transakcji, w którym go wykonywałeś, wbrew powszechnej opinii, że jest on transakalizowany - zwolniony IAM jest nie jest usuwany do czasu zatwierdzenia, więc można go wycofać, przywracając IAM. – Andrew

+0

Po prostu przeczytaj, a masz rację, na serwerze sql możesz wycofać skrócone. Miałem na myśli wyrocznię, gdzie nie jest to możliwe (zgodnie z dokumentacją). –

+0

Nie mogę używać TRUNCATE, ponieważ MyTable ma również klucz obcy Potrzebuję klauzuli WHERE dla danych filtrów –

3

Pierwsza ma wyraźnie lepszą wydajność.

Po wybraniu opcji USUŃ [MyTable] po prostu usunie wszystko bez sprawdzania identyfikatora. Drugi spowoduje stratę czasu i operacji na dysku, aby zlokalizować odpowiedni rekord za każdym razem przed usunięciem.

Pogarsza się także, ponieważ za każdym razem, gdy rekord zniknie ze środka stołu, silnik może chcieć skondensować dane na dysku, marnując czas i pracując ponownie.

Być może lepszym pomysłem byłoby usunięcie danych w oparciu o kolumny z klastrem indeksu w porządku malejącym. Następnie tabela będzie zasadniczo skrócona od końca przy każdej operacji usuwania.

+0

proszę mi powiedzieć dlaczego. –

+0

mówisz tak: "Może lepszym pomysłem byłoby usunięcie danych w oparciu o kolumny z indeksami klastrowanymi w porządku malejącym, a następnie tabela będzie zasadniczo obcięta od końca przy każdej operacji usuwania." możesz opisać więcej? –

+0

Silnik bazy danych przydziela dane fizycznie na dysku w klastrowej kolejności dysków. Jeśli usuwałeś rekordy o najwyższych wartościach indeksu, to w zasadzie prowadziłoby to do odcięcia końca pliku, bez kondensowania danych, które mogłyby wystąpić, gdybyś usunął coś w środku pliku. Jest to szczególnie ważne, gdy dodajesz rekordy, próbując je dołączyć na końcu pliku. Mogę sobie wyobrazić, że usuwanie rekordów z końca poprawiłoby również wydajność. –

3

Opcja 1 utworzy bardzo dużą transakcję i będzie miała duży wpływ na log/wydajność, a także eskalację blokad, aby tabela była niedostępna. Opcja 2 będzie wolniejsza, chociaż wygeneruje mniejszy wpływ na dziennik (przy założeniu trybu zbiorczego/pełnego)

Jeśli chcesz pozbyć się wszystkich danych, przycięcie tabeli MyTable będzie szybsze niż oba, chociaż ma brak możliwości filtrowania wierszy, zmiana meta danych z tyłu i zasadniczo upuszczenie IAM na podłogę dla danej tabeli.

+0

Nie mogę używać TRUNCATE, ponieważ MyTable ma również klucz obcy Potrzebuję klauzuli WHERE dla danych filtrów –

0

Pierwszy usunie wszystkie dane z tabeli i będzie miał lepsze osiągi, że drugi, który będzie usuwał tylko dane z określonego klucza.

Teraz, jeśli trzeba usunąć wszystkie dane z tabeli, a nie polegać na użyciu wycofywania myśleć o wykorzystaniu truncate table

21

Jeśli trzeba ograniczyć do tego, co wiersze trzeba usunąć, a nie zrobić kompletnym usunąć lub nie można używać truncate table (np tabela odwołuje się ograniczenie FK lub zawarte w widoku indeksowanego), a następnie można wykonać usuwać w kawałkach:

DECLARE @RowsDeleted INTEGER 
SET @RowsDeleted = 1 

WHILE (@RowsDeleted > 0) 
    BEGIN 
     -- delete 10,000 rows a time 
     DELETE TOP (10000) FROM MyTable [WHERE .....] -- WHERE is optional 
     SET @RowsDeleted = @@ROWCOUNT 
    END 

ogólnie obcina jest najlepszy sposób i użyłbym tego, jeśli to możliwe. Ale nie można go stosować we wszystkich scenariuszach. Zwróć też uwagę, że TRUNCATE zresetuje wartość IDENTITY dla tabeli, jeśli taka istnieje.

Jeśli korzystasz z SQL 2000 lub wcześniejszych, warunek TOP nie jest dostępny, więc możesz użyć SET ROWCOUNT zamiast tego.

DECLARE @RowsDeleted INTEGER 
SET @RowsDeleted = 1 
SET ROWCOUNT 10000 -- delete 10,000 rows a time 

WHILE (@RowsDeleted > 0) 
    BEGIN 
     DELETE FROM MyTable [WHERE .....] -- WHERE is optional 
     SET @RowsDeleted = @@ROWCOUNT 
    END 
+2

Możesz odejść od używania SET ROWCOUNT na rzecz SELECT/INSERT/UPDATE/DELETE TOP (N) ... Powód? Zajrzyj tutaj: http://msdn.microsoft.com/en-us/library/ms143729.aspx i tutaj: https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=282528 –

+0

Dobry punkt (zakładając SQL 2005 lub późniejszy), który jest prawdopodobnie bezpiecznym zakładem. Zaktualizowałem moją odpowiedź – AdaTheDev

0

Znaleziono post on Microsoft TechNet.

Zasadniczo zaleca:

  1. Korzystając SELECT INTO, skopiować dane, które chcesz zachować do tabeli pośredniej;
  2. Skróć tabelę źródłową;
  3. Skopiuj z INSERT INTO z tabeli pośredniej, dane do tabeli źródłowej;

..

BEGIN TRANSACTION 

SELECT * 
    INTO dbo.bigtable_intermediate 
    FROM dbo.bigtable 
    WHERE Id % 2 = 0; 

    TRUNCATE TABLE dbo.bigtable; 

    SET IDENTITY_INSERT dbo.bigTable ON; 
    INSERT INTO dbo.bigtable WITH (TABLOCK) (Id, c1, c2, c3) 
    SELECT Id, c1, c2, c3 FROM dbo.bigtable_intermediate ORDER BY Id; 
    SET IDENTITY_INSERT dbo.bigtable OFF; 
ROLLBACK TRANSACTION 
Powiązane problemy