¿Cómo ejecuto un script grande con muchas inserciones sin quedarse sin memoria?

28

Pregunta:

Tengo un script con alrededor de 45 mil inserciones de declaraciones selectas. Cuando intento ejecutarlo, aparece un mensaje de error que indica que me he quedado sin memoria. ¿Cómo puedo ejecutar este script?

Contexto:

  1. Se agregaron algunos campos de datos nuevos para que una aplicación funcione bien con otra aplicación que usa el cliente.
  2. Obtuve una hoja de cálculo de datos del cliente llena de datos que asignaron elementos de datos actuales a valores para estos nuevos campos.
  3. Hoja de cálculo convertida para insertar declaraciones.
  4. Si solo ejecuto algunas de las declaraciones, funciona, pero no todo el script.
  5. No. No hay errores tipográficos.

Si hay una forma diferente en la que debería cargar estos datos, no dude en castigarme y avisarme.

spaghetticowboy
fuente
Pregunta similar sobre SO: ( stackoverflow.com/questions/222442/… ) No estoy seguro de si la respuesta ayuda
jumpdart

Respuestas:

17

El tamaño de lote máximo para SQL Server 2005 es 65.536 * Tamaño de paquete de red (NPS), donde NPS suele ser 4KB. Eso funciona a 256 MB. Eso significaría que sus declaraciones de inserción promediarían 5.8 KB cada una. Eso no parece correcto, pero tal vez hay espacios extraños o algo inusual allí.

Mi primera sugerencia sería poner una declaración "IR" después de cada instrucción INSERT. Esto dividirá su único lote de 45,000 declaraciones INSERT en 45,000 lotes separados. Esto debería ser más fácil de digerir. Tenga cuidado, si uno de esos insertos falla, puede ser difícil encontrar al culpable. Es posible que desee protegerse con una transacción. Puede agregar esas declaraciones rápidamente si su editor tiene una buena búsqueda y reemplazo (que le permitirá buscar y reemplazar caracteres de retorno como \ r \ n) o una función de macro.

La segunda sugerencia es usar un asistente para importar los datos directamente desde Excel. El asistente crea un pequeño paquete SSIS para usted, detrás de escena, y luego lo ejecuta. No tendrá este problema.

estrecho de Darin
fuente
2
A GOdespués de cada declaración? Bueno, supongo que si los estás generando usando otro script, está bien. De lo contrario, solo pondría uno después de cada 1000 INSERTs. Con respecto a hacer que la transacción sea atómica y minimizar el tamaño de la transacción, ¿por qué no cargar todas las filas en una tabla temporal o variable de tabla y luego cargarlas de una vez desde allí a la tabla de destino?
Nick Chammas
Un 1000 es tan bueno como 1, pero más difícil de contar. Para ser honesto, podría salirse con la suya una declaración GO, a mitad de camino, cerca de la declaración 21,500. Me gusta la solución GO porque no requiere una edición complicada de la secuencia de comandos actual, ni contar las instrucciones INSERT (que podrían no asignarse directamente a números de línea).
estrecho de Darin
2
Seguramente, incluso una mala aproximación de 1000 declaraciones es lo suficientemente buena. :)
Nick Chammas
1
Agregar los GO fue una solución rápida y fácil. El script de 25mb se ejecuta en menos de 9 minutos sin problemas. Quería tenerlo como un script para mantenerlo dentro de nuestro proceso de implementación de parches estándar para cuando salga.
spaghetticowboy
14

BULK INSERTo bcpparecen opciones más apropiadas que 45,000 declaraciones de inserción

Si necesita seguir con las instrucciones de inserción, consideraría algunas opciones:

R: Use transacciones y envuelva lotes de 100 o 500 o 1000 extractos en cada uno para minimizar el impacto en el registro y el lote. p.ej

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: en lugar de declaraciones de inserción individuales, úselas UNION ALLpara 100 o 500 declaraciones a la vez, p. Ej.

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

He dejado el manejo de errores por brevedad, pero el punto es que nunca trataría de enviar un solo lote de 45,000 declaraciones individuales a SQL Server.

Aaron Bertrand
fuente
1
Lástima que el OP no pueda usar constructores de valor de tabla , una característica de 2008+. Todavía tendría que agrupar las inserciones en grupos de 1000 filas, que es el máximo que puede agrupar con un TVC.
Nick Chammas
Esa sería mi primera sugerencia hasta que vi la etiqueta de versión.
Aaron Bertrand
2
@NickChammas: el rendimiento de esas degradaciones no lineales con el número de cláusulas de valores BTW . Envié un elemento de conexión con una reproducción de insertar 1000 filas con 10 VARCHAR(800)columnas en 2008 con un tiempo de compilación de 12.5 minutos en mi instancia de desarrollo de 2008, ya que hace mucho trabajo innecesario comparando valores en lugar de simplemente continuar insertándolos (realiza mucho más rápido cuando se parametriza y no hay valores para mirar). Aunque ha mejorado mucho en 2012, el patrón no lineal todavía existe y debe corregirse en la versión posterior.
Martin Smith
9

No estoy seguro de por qué está obteniendo el error de falta de memoria, pero hay un enfoque más fácil.

Si puede exportar los datos de la hoja de cálculo a un formato delimitado (por ejemplo, csv), puede usar el asistente de importación de datos en SSMS para insertar los datos por usted:

Tarea de datos de importación SSMS.

datagod
fuente
eso es útil, pero no tengo acceso a las bases de datos de los clientes. Tengo que preparar parches y cargas de datos en scripts
spaghetticowboy
0

Usando múltiples SqlBulkCopy, cree una tabla temporal. Inserte datos nuevos en la tabla temporal, luego combine los datos en la tabla temporal con la existente. Ejemplo usando el método C # SqlBulkCopy.WriteToServer (DataTable) . Espero eso ayude

Hung Vu
fuente
0

Sí, podríamos hacer eso, intenté con un enfoque BCP (Bulk Copy Program) para evitar un problema de OutOfMemory .

Nota : Probado en SQL Server 2014.

En BCP, primero debemos exportar los datos de la base de datos de origen al archivo bcp (en la carpeta del directorio local) y luego debemos importar ese archivo bcp a la base de datos de destino.

ingrese la descripción de la imagen aquí

A continuación se detallan los pasos de la tarta:

Nota:

a) Asegúrese de que la tabla vacía esté presente en la base de datos de destino

b) Asegúrese de que la carpeta Temp esté presente en la unidad C

  1. Cree un archivo bat denominado Export_Data.bat con el comando que se muestra a continuación:

    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. Ejecute ese archivo bat, como resultado de eso se generará un archivo bcp en la carpeta Temp

  3. Luego, cree otro archivo bat llamado Import_Data.bat con el siguiente 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

¡Y aquí vamos!

Kms
fuente
Obteniendo error "Se requiere un nombre de tabla válido para las opciones de entrada, salida o formato". cuando trató de exportar datos.
Sen Jacob
1
¿Podría pegar el comando que intentó con todo el valor de atributo? Siga el siguiente ejemplo: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q En que [ExportDB -> Source DB, AddressCountry-> Table Present in Source DB, IN-L20054 -> Nombre de la máquina, "sa" es el nombre de usuario / pwd de DB]
Kms
No lo tengo ahora. Terminé usando la función de importación de datos en SSMS. Luego conecté el DB de destino (v14.0) al DB de origen (v.15.0) usando la conexión MS OLE DB y fue bastante rápido importar múltiples millones de filas de datos. ¡Gracias!
Sen Jacob