2009-07-20 8 views
10

Desidero modificare un campo da una tabella che contiene circa 4 milioni di record. Ho assicurato che tutti questi valori campi non sono NULL e desidera modificare questo campo per NOT NULLCampo ALTER di SQL Server NOT NULL richiede per sempre

ALTER TABLE dbo.MyTable 
ALTER COLUMN myColumn int NOT NULL 

... sembra prendere sempre per fare questo aggiornamento. Qualche modo per accelerarlo o sono bloccato solo a farlo durante la notte fuori orario?

Potrebbe anche causare un blocco del tavolo?

+1

Personalmente non avrei mai fare un cambiamento per la struttura della tabella in una tabella di grandi dimensioni esistente in qualsiasi momento, tranne le ore di punta.Anche se è abbastanza veloce, può far sì che gli utenti nel processo di fare le cose nel momento in cui apportate la modifica abbiano problemi. Qualsiasi modifica importante viene eseguita al meglio anche in modalità utente singolo. È molto meglio avere un periodo di manutenzione programmata in black out quando gli utenti non possono fare nulla (annunciato prima del corso e durante le ore non di punta), piuttosto che avere utenti infelici che stavano facendo qualcosa che sta ricevendo errori. – HLGEM

+0

La colonna che si sta modificando è coinvolta nei vincoli FK? – onupdatecascade

+1

Da un rapido test in profiler ci vuole un lock 'Sch-M' sul tavolo [che è fondamentalmente incompatibile con tutto] (http://msdn.microsoft.com/en-us/library/ms186396.aspx). Quindi deve leggere ogni pagina per determinare che tutte le righe vengano convalidate. –

risposta

4

È possibile modificare un campo e renderlo non nullo senza controllare i campi. Se sei davvero preoccupato di non farlo fuori orario, puoi aggiungere un vincolo al campo che controlla per assicurarsi che non sia nullo. Ciò ti consentirà di utilizzare l'opzione senza controllo e di non dover controllare ciascuna delle 4 milioni di righe per vedere se si aggiorna.

CREATE TABLE Test 
(
    T0 INT Not NULL, 
    T1 INT NUll 
) 

INSERT INTO Test VALUES(1, NULL) -- Works! 

ALTER TABLE Test 
    WITH NOCHECK 
     ADD CONSTRAINT N_null_test CHECK (T1 IS NOT NULL) 

    ALTER COLUMN T1 int NOT NULL 

INSERT INTO Test VALUES(1, NULL) -- Doesn't work now! 

Davvero si hanno due opzioni (aggiunto un terzo vedere Modifica):

  1. utilizzare il vincolo che impedirà eventuali nuove righe vengano aggiornati e lasciare inalterati quelli originali.
  2. Aggiorna le righe null a qualcos'altro e quindi applica l'opzione non nullo. Questo dovrebbe essere eseguito in orari non attivi, a meno che non ti dispiaccia che i processi siano bloccati fuori dal tavolo.

A seconda del proprio scenario specifico, entrambe le opzioni potrebbero essere migliori per voi. Non sceglierei l'opzione perché devi eseguirla in ore non lavorative. A lungo termine, il tempo dedicato all'aggiornamento nel cuore della notte sarà ben speso confrontato con il mal di testa che potresti affrontare prendendo una scorciatoia per risparmiare un paio d'ore.

Detto questo, se avete intenzione di andare con l'opzione numero due, potete ridurre al minimo la quantità di lavoro che fate in ore non lavorative. Dal momento che si deve fare in modo di aggiornare le righe di non nulli prima di modificare la colonna, si può scrivere un cursore lentamente (rispetto a fare tutto in una volta)

  1. passare attraverso ogni fila
  2. controllare per vedere se è nullo
  3. Aggiornare in modo appropriato. Questo ci vorrà un bel po ', ma non bloccherà l'intero blocco di tabella da altri programmi di accedervi. (Non dimenticare il suggerimento with(rowlock) tavolo!)

EDIT: ho pensato di una terza opzione: È possibile creare una nuova tabella con le colonne appropriate, e quindi esportare i dati dalla tabella originale a quello nuovo. Fatto ciò, puoi rilasciare la tabella originale e cambiare il nome di quello nuovo in quello vecchio. Per fare ciò dovrai disabilitare le dipendenze sull'originale e reimpostarle su quella nuova quando hai finito, ma questo processo ridurrà di molto la quantità di lavoro che devi fare nelle ore di pausa. Questo è lo stesso approccio utilizzato da sql server quando si effettuano le modifiche alle ordinazioni delle colonne alle tabelle attraverso lo studio di gestione. Per questo approccio, farei l'inserto in blocchi per assicurarmi che tu non provochi l'annullamento dello stress sul sistema e impedisca ad altri di accedervi. Poi, nelle ore libere, puoi lasciare l'originale, rinominare il secondo e applicare le dipendenze, ecc. Avrai ancora un po 'di lavoro fuori orario, ma sarà minimo rispetto all'altro approccio.

Collegamento all'uso di sp_rename.

+4

Se si utilizza NO CHECK, il vincolo non sarà considerato attendibile e non può essere utilizzato da Query Optimizer. Vedere http://sqlblog.com/blogs/tibor_karaszi/archive/2008/01/12/non-trusted-constraints-and-performance.aspx –

+3

Inoltre, la parola chiave NOCHECK non si applica a NULL/NOT NULL. Si applica solo ai vincoli che fanno parte della clausola CONSTRAINT. –

+0

Dalla domanda l'OP ha già assicurato che la loro colonna non contenga valori 'NULL' quindi non è sicuro della rilevanza della seconda parte della risposta? –

2

Ci scusiamo per l'abbattimento, ma:

  • dei modi per accelerarlo: No, se si desidera modificare la struttura della tabella stessa
  • o sono io bloccato facendo solo una notte durante il off- ore? Sì, e probabilmente è il migliore, come ha sottolineato @HLGEM
  • Potrebbe anche causare un blocco del tavolo? Si

Non direttamente rilevanti per voi (perché è di andare da NOT NULL NULL), ma interessante lettura su questo argomento: http://beyondrelational.com/blogs/sankarreddy/archive/2011/04/05/is-alter-table-alter-column-not-null-to-null-always-expensive.aspx

E infine un po 'di storia antica - su una questione equivalente in un forum in 2005, lo stesso suggerimento è stato fatto come @ Kevin offerto sopra - utilizzando un vincolo insteadof rendendo la colonna stessa non annullabile: http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=50671

4

l'unico modo per farlo "in fretta" (*), che io sappia è di gran

  • creazione di una tabella 'shadow' che ha il layout richiesto
  • aggiunta di un trigger alla tabella di origine in modo che qualsiasi operazione di inserimento/aggiornamento/eliminazione venga copiata nel tavolo shadow (attenzione a catturare eventuali NULL che potrebbero apparire popup!)
  • copia tutti i dati dall'origine alla tabella shadow, potenzialmente in blocchi di dimensioni ridotte (assicurati di poter gestire i dati già copiati dal/i trigger/i, assicurati che i dati si adattino alla nuova struttura (ISNULL (?))
  • di script fuori tutte le dipendenze da/per altre tabelle
  • quando tutto è fatto, effettuare le seguenti operazioni all'interno di una transazione esplicita:!
    • ottenere un blocco di tabella esclusiva sulla sorgente-tavolo e uno sul shadowtable
    • eseguire gli script di abbandonare le dipendenze alla fonte-tavolo
    • rinominare la fonte-tavolo a qualcos'altro (ad esempio, il suffisso _old)
    • rinominare la tabella ombra al nome originale del fonte-tavolo
    • eseguire gli script per creare tutte le dipendenze di nuovo

Si potrebbe desiderare di fare l'ultimo passo al di fuori della transazione s potrebbe richiedere un po 'di tempo a seconda della quantità e delle dimensioni delle tabelle che fanno riferimento a questa tabella, i primi passi non richiedono molto tempo

Come sempre, probabilmente è meglio fare una prova su un test -server first =)

PS: si prega di non essere tentati di ricreare gli FK con NOCHECK, li rende inutili in quanto l'ottimizzatore non si fida di loro né li considera quando costruisce un piano di query.

(*: dove arriva rapidamente a: con il tempo di inattività minimo possibile)

+0

Avevo giocato con questa idea per la scorsa settimana, ma nella nostra situazione specifica abbiamo riscontrato dei problemi perché potrebbero esserci più modifiche alle colonne che non si conoscono l'una sull'altra, quindi la creazione dinamica dei trigger "anziché" sarebbe impossibile (questo è tutto da automatizzare). Stamattina mi è venuto in mente che poteva essere guidato da tavolo e stavo per postare la risposta solo per scoprire che mi hai battuto :) –