2010-01-17 15 views
9

Usuwanie działania gdzie unt_uid jest nulljak do usuwania 8.500.000 rekordów z jednej tabeli na serwerze sql

byłby najszybszy sposób, ale nikt nie może uzyskać dostępu do bazy danych/tabeli aż ta wypowiedź zakończył więc jest to no- udać się.

Zdefiniowałem kursor, aby wykonać to zadanie w czasie pracy, ale w każdym razie wpływ na wydajność jest duży. Jak usunąć ten rekord, aby zagwarantować normalne korzystanie z tej bazy danych?

To serwer SQL-2005 na 32-bitowym Win2003. Drugie pytanie brzmi: jak długo byś oszacował wykonanie tej pracy (6 godzin lub 60 godzin)? (Tak, wiem, że zależy to od obciążenia, ale zakładam, że jest to środowisko małego biznesu).

Odpowiedz

9

Możesz to zrobić w porcjach. Na przykład, co 10 sekund wykonać:

delete from activities where activityid in 
    (select top 1000 activityid from activities where unt_uid is null) 

Oczywiście określić liczbę wierszy (I samowolnie wybrał 1000) i przedział (wybrałem 10 sekund), co sprawia, że ​​największy sens dla danej aplikacji.

+0

Dzięki za tę podpowiedź. Zrobiłbym to za pomocą kursora, który wybierze top 1000 i otoczy go chwilowo (wybierz liczbę (*) ...)> 0. Myślę, że dziennik transakcji jest opowieścią annother: nie można zrobić usunięcie bez logowania, podczas gdy normalne korzystanie z bazy danych jest regularnie rejestrowane? – Ice

+0

@ice Nie sądzę. Możesz wyłączyć dzienniki dla aktualizacji zbiorczych, ale bez usuwania. – Keltex

5

Być może zamiast usuwać rekordy z tabeli, możesz utworzyć nową identyczną tabelę, wstawić rekordy, które chcesz zachować, a następnie zmienić nazwy tabel, aby nowa zastępowała starą. Zajmie to trochę czasu, ale czas przestoju w Twojej witrynie będzie minimalny (tylko przy wymianie stołów).

3

Kto może uzyskać dostęp do tabeli, zależy od trybu izolacji transakcji, jak sądzę.

Masz jednak całkowitą rację - wiele usunięć jest złe, szczególnie jeśli klauzula where oznacza, że ​​nie może korzystać z indeksu - oznacza to, że baza danych prawdopodobnie nie będzie w stanie zablokować tylko tych wierszy, które trzeba usunąć, więc skończy się to, że weźmiesz duży zamek na cały stół.

Moja najlepsza rekomendacja to przeprojektowanie aplikacji, aby nie trzeba było usuwać tych wierszy lub ewentualnie żadnych wierszy.

Można to zrobić, dzieląc tabelę na partycje, tak aby można było po prostu upuścić partycje, lub użyć polecenia "skopiuj wiersze, które chcesz zachować, a następnie upuść tabelę" przepisu sugerowanego przez innych.

+0

Partycja to świetny pomysł: http://msdn.microsoft.com/en-us/library/ms345146(SQL.90).aspx –

+0

Partycjonowanie to JEDEN sposób uniknięcia konieczności usuwania, ale nie jedyny jeden. – MarkR

0

W środowisku małych firm dziwne wydaje się usuwanie 500 000 wierszy w standardowym zachowaniu operacyjnym bez wpływu na innych użytkowników. Zwykle w przypadku dużych rozmiarów robimy nową tabelę i używamy TRUNCATE/INSERT lub sp_rename do zastąpienia starej.

Powiedziawszy, że w specjalnym przypadku, jeden z moich miesięcznych procesów może regularnie usuwać 200 milionów wierszy w partiach o długości około 3 metrów, jeśli wykryje, że musi ponownie uruchomić proces, który wygenerował te 200-metrowe wiersze. Jest to jednak proces dla pojedynczego użytkownika w dedykowanej bazie danych hurtowni danych i nie nazwałbym go scenariuszem małego biznesu.

Ja po drugie odpowiedzi zalecam poszukiwanie alternatywnych podejść do twojego projektu.

2

Używam techniki "skubania". Od http://sqladvice.com/blogs/repeatableread/archive/2005/09/20/12795.aspx:

DECLARE @target int 
SET @target = 2000 
DECLARE @count int 
SET @count = 2000 

WHILE @count = 2000 BEGIN 

DELETE FROM myBigTable 
WHERE targetID IN 
(SELECT TOP (@target) targetID 
    FROM myBigTable WITH(NOLOCK) 
    WHERE something = somethingElse) 

SELECT @count = @@ROWCOUNT 
WAITFOR DELAY '000:00:00.200' 

END 

Używałem go dokładnie tego typu scenariusz. Ważne jest zachowanie numeru WAITFOR, ponieważ umożliwia ono wykonywanie innych zapytań między usunięciami.

+0

Nie powinieneś zamiast tego sprawdzać '@count <> 0'? W przeciwnym razie możesz mieć kilka wierszy w lewo, jeśli liczba wierszy nie dzieli się równomiernie z 2000 –

+0

To faktycznie działa. Rozważmy przypadek, w którym ostatnie DELETE ma 1337 wierszy. SELECT @ count = @@ rowcount dostaniesz 1337, kończąc pętlę while. –

+0

Oczywiście, liczba wierszy @@ będzie wyłączona, jeśli wystąpią jakiekolwiek wyzwalacze lub kaskadowe usuwanie. –

0

Utworzę dla tego zadanie i zaplanuję jego uruchomienie w godzinach poza godzinami pracy. Ale nie sugerowałbym, aby usunąć w używanym stole. Przenieś wiersze, które chcesz zachować do nowej tabeli i całkowicie upuść bieżącą tabelę z dużą ilością wierszy, które chcesz usunąć.

+0

Myślę, że to było opisane w [tej innej odpowiedzi] (http: // stackoverflow.com/a/2082767/573261) – RichardTheKiwi

Powiązane problemy