2009-10-29 14 views
19

Ho una tabella di SQL Server in produzione con milioni di righe e risulta che devo aggiungere una colonna. Oppure, per essere più precisi, ho bisogno di aggiungere un campo all'entità rappresentata dalla tabella.Come si aggiunge una colonna alla tabella sql server grande

Sinteticamente questo non è un problema, e se la tabella non avesse così tante righe e non fosse in produzione, sarebbe facile.

Davvero quello che sto cercando è la linea d'azione. Esistono molti siti Web con tabelle estremamente grandi e devono aggiungere campi di volta in volta. Come lo fanno senza sostanziali tempi di inattività?

Una cosa che dovrei aggiungere, non volevo che la colonna consentisse valori nulli, il che significherebbe che avrei bisogno di avere un valore predefinito.

Quindi ho bisogno di capire come aggiungere una colonna con un valore predefinito in modo tempestivo, o ho bisogno di capire un modo per aggiornare la colonna in un secondo momento e quindi impostare la colonna per non consentire i null .

risposta

23
ALTER TABLE table1 ADD 
    newcolumn int NULL 
GO 

non dovrebbe prendere così a lungo ... Che richiede molto tempo è quello di inserire colonne nel mezzo di altre colonne ... b/c, allora il motore ha bisogno di creare una nuova tabella e copiare i dati la nuova tabella.

+6

Sai cosa, hai ragione. Finché la colonna ha un valore di NULL, viene aggiunta abbastanza velocemente. Ma se ottengo un valore predefinito, richiede molto tempo. Quindi il vero problema che devo pianificare è come aggiungere un valore predefinito alla colonna. –

+10

Aggiungere la colonna e quindi eseguire batch UPDATE relativamente piccoli per popolare la colonna con un valore predefinito. Ciò dovrebbe impedire qualsiasi rallentamento evidente. –

+0

Grazie Agent_9191, sembra un approccio abbastanza decente. –

11

L'unica vera soluzione per il tempo di attività continuo è la ridondanza .

Riconosco la risposta di @ Nestor che l'aggiunta di una nuova colonna non dovrebbe richiedere molto tempo in SQL Server, ma nonostante ciò, potrebbe ancora essere un'interruzione non accettabile su un sistema di produzione. Un'alternativa è fare la modifica in un sistema parallelo, e quindi una volta completata l'operazione, scambia il nuovo con il vecchio.

Ad esempio, se è necessario aggiungere una colonna, è possibile creare una copia della tabella, quindi aggiungere la colonna a quella copia e quindi utilizzare sp_rename() per spostare il vecchio tavolo da parte e la nuova tabella in posizione.

Se si dispone di vincoli di integrità referenziale che puntano a questa tabella, ciò può rendere lo scambio ancora più complicato. Probabilmente devi abbandonare brevemente i vincoli mentre cambi i tavoli.

Per alcuni tipi di aggiornamenti complessi, è possibile duplicare completamente il database su un host server separato. Una volta che è pronto, basta scambiare le voci DNS per i due server e voilà!

ho sostenuto una società di borsa nel 1990 che gestivano tre duplicati server di database in ogni momento. Quel modo in cui potevano implementare gli aggiornamenti su un server, mantenendo un server di produzione e un server di failover . Le loro operazioni hanno avuto una procedura standard di rotazione delle tre macchine tramite la produzione, il failover e i ruoli di manutenzione ogni giorno. Quando avevano bisogno di aggiornare l'hardware , lo schema del database , ci sono voluti tre giorni per propagare la modifica attraverso i loro server , ma potevano farlo senza l'interruzione del servizio . Tutto grazie allo alla ridondanza.

+2

Come hai recuperato le transazioni perse durante la manutenzione? Replica standard? –

+0

Una borsa non ha bisogno di operare 24/7. Si chiudono alla campana. –

+0

Doh :-) Pensieri su come gestirlo per i sistemi 24/7? –

7

"Aggiungere la colonna e quindi eseguire relativamente piccoli lotti UPDATE per popolare la colonna con un valore di default. Questo dovrebbe prevenire eventuali rallentamenti evidenti"

E dopo che si deve impostare la colonna a NOT NULL che verrà attivato in un'unica grande transazione. Quindi tutto funzionerà molto velocemente finché non lo fai, quindi probabilmente hai guadagnato davvero poco. Lo so solo dall'esperienza di prima mano.

È possibile rinominare la tabella corrente da X a Y. È possibile eseguire questa operazione con questo comando sp_RENAME '[OldTableName]', '[NewTableName]'.

Ricreare la nuova tabella come X con la nuova colonna impostata su NOT NULL e quindi inserire in batch da Y a X e includere un valore predefinito nel proprio inserimento per la nuova colonna o posizionare un valore predefinito nella nuova colonna quando si ricreare tabella X.

Ho eseguito questo tipo di modifica su una tabella con centinaia di milioni di righe. Ci è voluta ancora un'ora, ma non è saltato fuori il nostro registro. Quando ho provato a cambiare la colonna in NOT NULL con tutti i dati nella tabella ci sono volute più di 20 ore prima che avessi ucciso il processo.

Hai provato solo aggiungendo una colonna riempiendo di dati e impostando la colonna su NOT NULL?

Quindi alla fine non penso ci sia un proiettile magico.

3

selezionare in una nuova tabella e rinominare. Esempio, aggiunta della colonna i alla tabella A:

select *, 1 as i 
into A_tmp 
from A_tbl 

//Add any indexes here 

exec sp_rename 'A_tbl', 'A_old' 
exec sp_rename 'A_tmp', 'A_tbl' 

Dovrebbe essere veloce e non toccherà il registro delle transazioni come potrebbe essere l'inserimento in lotti. (l'ho fatto solo oggi con un tavolo da 70 milioni di righe nel < 2 min).

È possibile eseguire il wrap in una transazione se è necessario che sia un'operazione in linea (qualcosa potrebbe cambiare nella tabella tra la selezione e la rinomina).

+1

Non riesco a capire questo. Si inserisce in 'A_tmp' da' A_tbl'. Ma poi si rinomina da 'A_tbl' a' A_old' e quindi si rinomina 'A_old' in' A_tbl'. Non dovrebbe essere l'ultimo rinominare da 'A_tmp' a' A_tbl'? – Junto

+0

@Junto sì, l'ho risolto –

0

Un'altra tecnica consiste nell'aggiungere la colonna in una nuova tabella correlata (presupporre una relazione uno a uno che è possibile applicare assegnando all'FK un indice univoco). È quindi possibile popolare questo in lotti e quindi è possibile aggiungere il join a questa tabella dove si desidera che i dati vengano visualizzati. Nota Considererei solo questo per una colonna che non vorrei utilizzare in tutte le query sulla tabella originale o se la larghezza del record della mia tabella originale stava diventando troppo grande o se aggiungevo più colonne.

6

Non volevo che la colonna consentisse valori nulli, il che significherebbe che avrei bisogno di avere un valore predefinito.

Aggiunta di una colonna di NOT NULL con un DEFAULT vincolo ad un tavolo di un qualsiasi numero di righe (anche miliardi) è diventato una partenza molto più facile in SQL Server 2012 (ma solo per Enterprise Edition), in quanto ha permesso di essere un'operazione online (nella maggior parte dei casi) in cui, per le righe esistenti, il valore verrà letto dai metadati e non verrà effettivamente archiviato nella riga fino a quando la riga non viene aggiornata o l'indice cluster viene ricostruito.Piuttosto che parafrasare più, qui è la relativa sezione dalla pagina MSDN per ALTER TABLE:

Aggiungere colonne NOT NULL come un'operazione in linea

partire da SQL Server 2012 Enterprise Edition, l'aggiunta di un NOT NULL la colonna con un valore predefinito è un'operazione online quando il valore predefinito è una costante di runtime . Ciò significa che l'operazione è completata quasi istantaneamente indipendentemente dal numero di righe nella tabella. Questo perché le righe esistenti nella tabella non vengono aggiornate durante l'operazione; il valore predefinito viene invece memorizzato solo nei metadati della tabella e il valore viene cercato, se necessario, nelle query che accedono a tali righe. Questo comportamento è automatico; non è richiesta alcuna sintassi aggiuntiva per implementare l'operazione online oltre la sintassi ADD COLUMN. Una costante di runtime è un'espressione che produce lo stesso valore in fase di esecuzione per ogni riga della tabella indipendentemente dal suo determinismo. Ad esempio, l'espressione costante "I miei dati temporanei" o la funzione di sistema GETUTCDATETIME() sono costanti di runtime. Al contrario, le funzioni NEWID() o NEWSEQUENTIALID() non sono costanti di runtime perché viene prodotto un valore univoco per ogni riga nella tabella. L'aggiunta di una colonna NOT NULL con un valore predefinito che non è una costante di runtime viene sempre eseguita offline e un blocco esclusivo (SCH-M) viene acquisito per la durata dell'operazione.

Mentre le righe esistenti fanno riferimento al valore memorizzato nei metadati, il valore predefinito viene archiviato sulla riga per tutte le nuove righe che vengono inserite e non specificare un altro valore per la colonna. Il valore predefinito memorizzato nei metadati viene spostato su una riga esistente quando la riga viene aggiornata (anche se la colonna effettiva non è specificata nell'istruzione UPDATE) o se la tabella o l'indice cluster viene ricostruita.

Colonne di tipo varchar (max), nvarchar (max), varbinary (max), XML, testo, ntext, immagine, hierarchyid, geometria, geografia, o CLR UDTS, non possono essere aggiunti in un'operazione in linea. Una colonna non può essere aggiunta online se così facendo fa sì che la dimensione massima possibile della riga superi il limite di 8.060 byte. In questo caso, la colonna viene aggiunta come operazione offline.

+0

Che dire di una colonna nulla SQL Server 2012 in edizione standard 14M righe, 24x7 alta concorrenza? Sarà un tempo di inattività evidente a causa del blocco dello schema? – Horaciux

+1

@Horaciux Una colonna 'NULL' invece di' NOT NULL'? Se sto capendo correttamente, questo è un non-problema. È solo meta-dati ed è piuttosto istantaneo. Prima che SQL Server 2012 uscisse con la possibilità di aggiungere una colonna 'NULL' all'istante fintanto che ha un valore predefinito, l'unico modo per aggiungere una colonna senza bloccare nulla era aggiungendolo come' NULL'. Ma poi è stato necessario popolarlo tramite job SQL Agent o set di 3000 righe per ogni UPDATE (per evitare l'escalation dei blocchi). Quindi no, non devi preoccuparti di una colonna 'NULL', almeno non nella mia esperienza. –

Problemi correlati