2009-12-17 3 views
14

Ho un database SQL Server di grandi dimensioni con una tabella di circa 45 milioni di record. Sto archiviando questa tabella e devo rimuovere tutte le voci più grandi di due anni fa. Ho l'inserimento nella mia tabella di archivio che funziona bene, ma sto avendo problemi con l'efficienza quando si elimina.SQL Server 2000 Delete Top (1000)

Il mio problema si trova all'interno degli indici attualmente sul tavolo. Vorrei eliminare (e inserire l'archivio) in 1000 blocchi di record. Per fare questo, ho bisogno di determinare i "primi" 1000 record che soddisfano il requisito (maggiore di due anni). Il timbro DateTime sulla riga è un indice cluster, quindi è ottimo per afferrare le righe. Tuttavia SQL 2000 non consente CANCELLA TOP 1000 .... quindi ho bisogno di fare qualcosa di simile:

DELETE FROM <table> WHERE [UniqueID] IN 
(SELECT TOP 1000 [UniqueID] FROM <table> WHERE [DateTime] < @TwoYearsAgo) 

Questo grande lavoro, se UniqueID è stato indicizzato. Dal momento che non lo è, questo richiede molto tempo (è la scansione della tabella per ciascuno dei 1000 record da eliminare). Non ci sono altri indici sulla tabella che identificano in modo univoco i record. Mi è stato detto che sarebbe troppo costoso calcolare un indice su UniqueID, dato che si tratta di un DB live. Qualcuno può indicare un modo per ottimizzare questa query?

+3

Come mai sei riuscito ad aggiungere una colonna denominata UniqueID, che identifica le righe, ma non ha indice? Non hai una chiave primaria? –

+0

Non ho progettato il tavolo, sono un nuovo programmatore del progetto incaricato di archiviarlo. Si tratta di una tabella di scrittura pesante (utilizzata per la registrazione), ma non molto in termini di letture, pertanto la creazione di un indice aggiuntivo (o PK, per quella materia) causerebbe aggiunte non necessarie al tempo di inserimento. Ancora non è come l'avrei progettato, ma è quello che è. – Kevin

+3

La persona che ti ha detto che era troppo costoso da indicizzare sta mentendo. :-) – onupdatecascade

risposta

17

Che ne dici di riscrivere la query?

SET ROWCOUNT 1000 
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo 

Vedere documentazione su SET ROWCOUNT (Transact-SQL).

Si noti inoltre che, per la documentazione di DELETE, supporta la clausola TOP, ma è apparentemente nuova per SQL Server 2005 e versioni successive. Dico questo perché sembra che non sia supportato sul tuo server database, ma hai provato a usarlo? Non ho accesso alla documentazione di SQL Server 2000, quindi non sono sicuro se è supportato su quella versione. Potrebbe benissimo non esserlo.

DELETE TOP (1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo 

nota la differenza con il modo TOP su selezionare può essere scritto, senza la parentesi. Per UPDATE, DELETE e INSERT, l'espressione deve essere tra parentesi, anche se è solo un numero costante come sopra.

+0

Sto anche spingendo per passare a Server 2008, ma molto probabilmente andremo a tagliare il database prima di spostarlo in una nuova istanza. – Kevin

+0

Sì, ho provato sia con che senza parentesi, senza risultato. – Kevin

+0

Si noti che in base ai documenti MSDN 'SET ROWCOUNT' non influirà più sulle istruzioni di inserimento, cancellazione e aggiornamento nella prossima versione successiva a sql server 2012. Quindi se si desidera una query che funzioni su tutte le versioni si dovrebbe fare il' cancella da (seleziona in alto ...) ' – ChrisWue

2

Si potrebbe utilizzare SET ROWCOUNT:

SET ROWCOUNT 1000 
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo 
+0

Ho visto questo suggerimento da qualche parte, ma avevo l'impressione che fosse pericoloso in un database live. Lo esaminerò di più, grazie per il suggerimento. – Kevin

3

si può anche fare

DELETE TOP(1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo 

Dio solo sa perché usano superiore (x) per cancellare e x superiore per selezionare, la maggior parte delle persone non lo fanno sembra anche sapere di questa funzionalità!

modifica: Apparentemente il suo 2005 +, quindi probabilmente dovresti ignorarlo.

1

Ho dovuto fare qualcosa di simile un po 'di tempo fa - inserire inserti leggeri e cancella per spostare vecchi record in una tabella di archivio. Anche se controintuitivo, la soluzione più veloce e meno impattante ho trovato è stato:

  1. Fai un piccolo tavolo #temp con i valori di ID per la parte superiore (x) righe. Se l'ID non può essere realmente indicizzato nello scenario, è possibile utilizzare invece la data AND ID, quindi la combinazione di entrambi può utilizzare un indice.

  2. cominciano tran

  3. Inserire nella tabella di archivio dove ID e la data (#temp)

  4. Elimina dalla tabella principale dove ID e la data (#temp)

  5. commettere

  6. Truncate #temp

  7. Repe a

Avere la tabella temporanea di mettere in scena le identificatori di riga è più lavoro totale di una eliminazione dritto, ma rende il processo molto leggero nei casi in cui si desidera di chip solo via un po 'alla volta senza bloccare.

Anche io sono d'accordo con Lasse: non è possibile vedere il punto di un ID univoco senza indice, e quindi senza vincoli, per applicarlo.

+0

Ho provato qualcosa di simile a questo con una tabella temporanea dichiarata localmente, ma senza avere un identificatore univoco indicizzato, non è stato di grande aiuto. Proverò ad usare sia la data che l'ID univoco, vedi se questo mi porta ovunque. Grazie! – Kevin

8

È possibile eliminare una sottoquery:

DELETE <table> FROM (
    SELECT TOP 1000 * 
    FROM <table> 
    WHERE [DateTime] < @TwoYearsAgo); 

vedere l'esempio E: a SQL 2000 DELETE Syntax. Questo è consigliato rispetto all'approccio SET ROWCOUNT. In SQL 2005 e versioni successive è possibile specificare direttamente il TOP in DELETE.

0

Mi chiedo se è necessario attenersi al requisito del record di 1000 record. Se è lì per la ragione di carico del server e tipo di arbitrario, si consiglia di provare quanto segue, dal momento che si dispone già di un indice cluster su [DateTime]:

DELETE FROM <table> 
WHERE [DateTime] < @TwoYearsAgo 
and [DateTime] < (select dateadd(day, 1, min([DateTime])) from <table>) 
0

Per garantire la compatibilità, le parentesi sono opzionali nelle istruzioni SELECT. Si consiglia di utilizzare sempre le parentesi per TOP nelle istruzioni SELECT per coerenza con l'utilizzo richiesto nelle frasi INSERT, UPDATE, MERGE e DELETE in cui sono richieste le parentesi.

USE AdventureWorks; 
GO 
DELETE TOP (20) 
FROM Purchasing.PurchaseOrderDetail 
WHERE DueDate < '20120701'; 
GO