Comment exécuter un gros script avec de nombreuses insertions sans manquer de mémoire?


28

Question:

J'ai un script avec environ 45 000 insertions de déclarations sélectionnées. Lorsque j'essaye de l'exécuter, j'obtiens un message d'erreur indiquant que je n'ai plus de mémoire. Comment puis-je exécuter ce script?

Le contexte:

  1. Ajout de nouveaux champs de données pour rendre une application agréable avec une autre application utilisée par le client.
  2. Vous avez obtenu une feuille de calcul du client pleine de données qui a mappé les éléments de données actuels aux valeurs de ces nouveaux champs.
  3. Feuille de calcul convertie pour insérer des instructions.
  4. Si je n'exécute que certaines des instructions, cela fonctionne, mais pas tout le script.
  5. Non, il n'y a pas de fautes de frappe.

S'il y a une manière différente de charger ces données, n'hésitez pas à me châtier et à me le faire savoir.


Question similaire sur SO: ( stackoverflow.com/questions/222442/… ) Je ne sais pas si la réponse aide
jumpdart

Réponses:


17

La taille de lot maximale pour SQL Server 2005 est de 65 536 * taille de paquet réseau (NPS), où NPS est généralement de 4 Ko. Cela équivaut à 256 Mo. Cela signifierait que vos instructions d'insertion seraient en moyenne de 5,8 Ko chacune. Cela ne semble pas juste, mais il y a peut-être des espaces étrangers ou quelque chose d'inhabituel là-dedans.

Ma première suggestion serait de mettre une instruction "GO" après chaque instruction INSERT. Cela divisera votre lot unique de 45 000 instructions INSERT en 45 000 lots distincts. Cela devrait être plus facile à digérer. Soyez prudent, si l'un de ces inserts échoue, vous aurez peut-être du mal à trouver le coupable. Vous voudrez peut-être vous protéger avec une transaction. Vous pouvez ajouter ces instructions rapidement si votre éditeur a une bonne fonction de recherche et remplacement (qui vous permettra de rechercher et de remplacer les caractères de retour comme \ r \ n) ou une fonction de macro.

La deuxième suggestion consiste à utiliser un assistant pour importer les données directement à partir d'Excel. L'Assistant crée un petit package SSIS pour vous, dans les coulisses, puis l'exécute. Il n'aura pas ce problème.


2
Un GOaprès chaque déclaration? Eh bien, je suppose que si vous les générez en utilisant un autre script, c'est OK. Sinon, je n'en mettrais qu'un après toutes les 1000 INSERTs. Pour ce qui est de rendre la transaction atomique et de minimiser la taille de la transaction, pourquoi ne pas charger toutes les lignes dans une table temporaire ou une variable de table, puis les charger en une seule fois de là vers la table cible?
Nick Chammas

Un 1000 est aussi bon qu'un 1, mais plus difficile à compter. Pour être honnête, il pourrait s'en tirer avec une seule déclaration GO, à mi-chemin, près de la déclaration 21 500. J'aime le correctif GO car il ne nécessite pas de modification compliquée du script actuel, ni de compter les instructions INSERT (qui pourraient ne pas correspondre directement aux numéros de ligne).
détroit de

2
Certes, même une mauvaise approximation de 1000 déclarations est suffisante. :)
Nick Chammas

1
L'ajout des GO était une solution rapide et facile. Le script de 25 Mo s'exécute en un peu moins de 9 minutes sans aucun problème. Je voulais l'avoir comme script pour le garder dans notre processus de déploiement de correctifs standard quand il disparaîtrait.
spaghetticowboy

14

BULK INSERTou bcpsemblent des options plus appropriées que 45 000 instructions d'insertion.

Si vous devez vous en tenir aux instructions d'insertion, je considérerais quelques options:

R: Utilisez des transactions et encapsulez des lots de 100, 500 ou 1 000 relevés dans chacun pour minimiser l'impact sur le journal et le lot. par exemple

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: Au lieu d'instructions d'insertion individuelles, utilisez UNION ALLpour 100 ou 500 instructions à la fois, par exemple

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

J'ai laissé la gestion des erreurs par souci de concision, mais le fait est que je n'essaierais jamais d'envoyer un seul lot de 45 000 instructions individuelles à SQL Server.


1
Dommage que l'OP ne puisse pas utiliser les constructeurs de valeur de table , une fonctionnalité de 2008+. Il lui faudrait encore regrouper les insertions en groupes de 1000 lignes, ce qui est le maximum que vous pouvez regrouper avec un TVC.
Nick Chammas

Cela allait être ma première suggestion jusqu'à ce que je voie la balise de version.
Aaron Bertrand

2
@NickChammas - Les performances de celles-ci se dégradent de manière non linéaire avec le nombre de clauses de valeurs BTW . J'ai soumis un élément de connexion avec une repro d'insérer 1000 lignes avec 10 VARCHAR(800)colonnes sur 2008 avec un temps de compilation de 12,5 minutes sur mon instance de développement 2008 car il fait beaucoup de travail inutile en comparant les valeurs plutôt que de simplement les insérer (effectue beaucoup plus rapide une fois paramétré et aucune valeur à regarder). Bien que beaucoup amélioré en 2012, le modèle non linéaire existe toujours et devrait être corrigé dans la version après.
Martin Smith

9

Je ne sais pas pourquoi vous obtenez l'erreur de mémoire insuffisante, mais il existe une approche plus simple.

Si vous pouvez exporter les données de la feuille de calcul dans un format délimité (par exemple csv), vous pouvez utiliser l'assistant d'importation de données dans SSMS pour insérer les données pour vous:

Tâche d'importation de données SSMS.


c'est utile mais je n'ai pas accès aux bases de données des clients. Je dois préparer des correctifs et des charges de données dans des scripts
spaghetticowboy

0

À l'aide de plusieurs SqlBulkCopy, créez une table temporaire. Insérez de nouvelles données dans la table temporaire, puis fusionnez les données de la table temporaire dans la table existante. Exemple utilisant la méthode C # SqlBulkCopy.WriteToServer (DataTable) . J'espère que ça aide


0

Oui, nous avons pu le faire, j'ai essayé avec une approche BCP (Bulk Copy Program) afin d'éviter un problème OutOfMemory .

Remarque : essayé sur SQL Server 2014.

Dans BCP, nous devons d'abord exporter les données de la base de données source vers le fichier bcp (dans le dossier du répertoire local), puis nous devons importer ce fichier bcp dans la base de données de destination.

entrez la description de l'image ici

Voici les étapes de la marche du gâteau:

Remarque:

a) Assurez-vous que la table vide est présente dans la base de données de destination

b) Assurez-vous que le dossier Temp est présent dans le lecteur C

  1. Créez un fichier bat nommé Export_Data.bat avec la commande ci-dessous:

    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 

    pause

  2. Exécutez ce fichier bat, à la suite d'un fichier bcp sera généré dans le dossier Temp

  3. Créez ensuite un autre fichier bat nommé Import_Data.bat avec la commande suivante:

    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 

    Pause

Et c'est reparti!


Obtenir l'erreur "Un nom de table valide est requis pour les options d'entrée, de sortie ou de formatage." lorsqu'il a essayé d'exporter des données.
Sen Jacob

1
Pourriez-vous coller la commande que vous avez essayée avec toute la valeur d'attribut.Veuillez suivre l'exemple ci-dessous: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q Dans cette [ExportDB -> DB source, AddressCountry-> Table présente dans la DB source, IN-L20054 -> Nom de la machine, "sa" est le nom d'utilisateur / mot de passe de la DB]
Kms

Je ne l'ai pas maintenant. J'ai fini par utiliser la fonction d'importation de données dans SSMS. Ensuite, il a connecté la base de données cible (v14.0) à la base de données source (v.15.0) à l'aide de la connexion MS OLE DB et il a été assez rapide d'importer plusieurs millions de lignes de données. Merci!
Sen Jacob
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.