Come posso eseguire uno script di grandi dimensioni con molti inserimenti senza esaurire la memoria?


28

Domanda:

Ho una sceneggiatura con circa 45 mila inserti da dichiarazioni selezionate. Quando provo ad eseguirlo, viene visualizzato un messaggio di errore che indica che ho esaurito la memoria. Come posso eseguire questo script?

Contesto:

  1. Aggiunti alcuni nuovi campi dati per rendere piacevole l'app con un'altra app utilizzata dal client.
  2. Ho ricevuto un foglio di calcolo di dati dal client pieno di dati che associavano gli elementi di dati correnti ai valori per questi nuovi campi.
  3. Foglio di calcolo convertito per inserire istruzioni.
  4. Se eseguo solo alcune delle affermazioni, funziona, ma l'intero script no.
  5. No. Non ci sono errori di battitura.

Se c'è un altro modo in cui dovrei caricare questi dati, sentitevi liberi di castigarmi e di farmelo sapere.


Domanda simile su SO: ( stackoverflow.com/questions/222442/… ) Non sono sicuro se la risposta aiuta
jumpdart

Risposte:


17

La dimensione massima del batch per SQL Server 2005 è 65.536 * Network Packet Size (NPS), dove NPS è in genere 4KB. Funziona fino a 256 MB. Ciò significherebbe che le tue istruzioni insert avrebbero una media di 5,8 KB ciascuna. Non sembra giusto, ma forse ci sono spazi estranei o qualcosa di insolito lì dentro.

Il mio primo suggerimento sarebbe quello di inserire un'istruzione "GO" dopo ogni istruzione INSERT. Ciò spezzerà il tuo singolo batch di 45.000 istruzioni INSERT in 45.000 batch separati. Questo dovrebbe essere più facile da digerire. Fai attenzione, se uno di quegli inserti fallisce, potresti avere difficoltà a trovare il colpevole. Potresti voler proteggerti con una transazione. Puoi aggiungere rapidamente queste istruzioni se il tuo editor ha una buona ricerca e sostituzione (che ti permetterà di cercare e sostituire i caratteri di ritorno come \ r \ n) o una funzione macro.

Il secondo suggerimento è di utilizzare una procedura guidata per importare i dati direttamente da Excel. La procedura guidata crea un piccolo pacchetto SSIS per te, dietro le quinte, e quindi lo esegue. Non avrà questo problema.


2
A GOdopo ogni affermazione? Bene, immagino che se li stai generando usando un altro script che va bene. Altrimenti, ne metterei solo uno ogni 1000 INSERTs. Per quanto riguarda la realizzazione della transazione atomica e la riduzione al minimo delle dimensioni della transazione, perché non caricare tutte le righe in una tabella temporanea o in una tabella variabile e poi caricarle in un colpo da lì alla tabella di destinazione?
Nick Chammas,

Un 1000 vale quanto 1, ma è più difficile da contare. Ad essere onesti, potrebbe cavarsela con una sola dichiarazione GO, a metà strada, vicino alla dichiarazione 21.500. Mi piace la correzione GO perché non richiede una modifica complicata dello script corrente o il conteggio delle istruzioni INSERT (che potrebbero non essere mappate direttamente ai numeri di riga).
Darin Strrait,

2
Sicuramente, anche una cattiva approssimazione di 1000 affermazioni è abbastanza buona. :)
Nick Chammas,

1
L'aggiunta dei GO è stata una soluzione rapida e semplice. Lo script da 25 MB viene eseguito in poco meno di 9 minuti senza problemi. Volevo averlo come script per mantenerlo all'interno del nostro processo di distribuzione di patch standard per quando esce.
spaghetticowboy,

14

BULK INSERTo bcpsembrano opzioni più appropriate rispetto a 45.000 istruzioni insert.

Se è necessario attenersi alle istruzioni di inserimento, prenderei in considerazione alcune opzioni:

A: Utilizzare transazioni e avvolgere batch di 100 o 500 o 1000 istruzioni in ciascuna per ridurre al minimo l'impatto sul registro e sul batch. per esempio

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

B: Invece di singole istruzioni insert, utilizzare UNION ALLper 100 o 500 istruzioni alla volta, ad es

INSERT dbo.table(a, ...)
SELECT 1, ...
UNION ALL SELECT 2, ...
...
UNION ALL SELECT 500, ...
GO

INSERT dbo.table(a, ...)
SELECT 501, ...
UNION ALL SELECT 502, ...
...
UNION ALL SELECT 1000, ...
GO

Ho lasciato errori nella gestione per brevità, ma il punto è che non avrei mai provato a inviare un singolo batch di 45.000 singole istruzioni a SQL Server.


1
Peccato che l'OP non possa usare costruttori di valori di tabella , una funzione 2008+. Dovrebbe comunque raggruppare gli inserti in gruppi di 1000 righe, che è il massimo che puoi raggruppare insieme a un TVC.
Nick Chammas,

Sarebbe stato il mio primo suggerimento fino a quando non ho visto il tag della versione.
Aaron Bertrand

2
@NickChammas - Le prestazioni di questi si riducono in modo non lineare con il numero di clausole dei valori BTW . Ho inviato un elemento di connessione con una replica di inserimento di 1000 righe con 10 VARCHAR(800)colonne nel 2008 con un tempo di compilazione di 12,5 minuti sulla mia istanza di sviluppo del 2008 in quanto fa un sacco di lavoro non necessario confrontando i valori piuttosto che andare avanti con l'inserimento di loro (esegue molto più veloce se parametrizzato e nessun valore da guardare). Sebbene molto migliorato nel 2012, il modello non lineare esiste ancora e dovrebbe essere corretto nella versione successiva.
Martin Smith,

9

Non sono sicuro del motivo per cui si sta verificando l'errore di memoria insufficiente, ma esiste un approccio più semplice.

Se puoi esportare i dati dal foglio di calcolo in un formato delimitato (ad esempio csv) puoi utilizzare la procedura guidata di importazione dei dati in SSMS per inserire i dati per te:

Attività di importazione dati SSMS.


questo è utile ma non ho accesso ai database dei clienti. Devo preparare patch e dataload negli script
spaghetticowboy il

0

Utilizzando più SqlBulkCopy, creare una tabella temporanea. Inserire nuovi dati nella tabella temporanea, quindi unire i dati nella tabella temporanea in quella esistente. Esempio usando il metodo C # SqlBulkCopy.WriteToServer (DataTable) . Spero che sia d'aiuto


0

Sì, abbiamo potuto farlo, ho provato con un approccio BCP (Bulk Copy Program) al fine di evitare un problema OutOfMemory .

Nota : provato su SQL Server 2014.

In BCP, prima dobbiamo esportare i dati del database di origine nel file bcp (nella cartella della directory locale) e quindi importare quel file bcp nel database di destinazione.

inserisci qui la descrizione dell'immagine

Di seguito sono riportati i passaggi della torta:

Nota:

a) Assicurarsi che nel database di destinazione sia presente una tabella vuota

b) Assicurarsi che Temp cartella è presente in C unità

  1. Crea un file bat chiamato Export_Data.bat con il comando mostrato di seguito:

    bcp.exe [Source_DataBase_Name].[dbo].[TableName] OUT "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    pausa

  2. Esegui quel file bat, di conseguenza un file bcp verrà generato nella cartella Temp

  3. Quindi crea un altro file bat chiamato Import_Data.bat con il seguente comando:

    bcp.exe [Destination_DataBase_Name].[dbo].[TableName] IN "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Pausa

Ed eccoci qui!


Errore "È richiesto un nome di tabella valido per le opzioni di ingresso, uscita o formattazione". quando si tenta di esportare dati.
Sen Jacob,

1
Potresti incollare il comando che hai provato con tutto il valore dell'attributo. Segui l'esempio seguente: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q In quanto [ExportDB -> DB di origine, AddressCountry-> Tabella presente nel DB di origine, IN-L20054 -> Nome macchina, "sa" è il nome utente / pwd del DB]
Kms

Non ce l'ho adesso. Ho finito per usare la funzione di importazione dei dati in SSMS. Quindi collegato il DB di destinazione (v14.0) al DB di origine (v.15.0) utilizzando la connessione MS OLE DB ed è stato abbastanza veloce importare milioni di righe di dati. Grazie!
Sen Jacob,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.