2009-10-21 26 views
5

Tengo un par de tablas de gran tamaño (188m y 144m filas) que necesito para llenar de puntos de vista, pero cada vista contiene unos pocos cientos de millones de filas (reunir datos pseudo-modelados dimensionalmente en una forma plana). Las claves en cada tabla tienen más de 50 bytes compuestos de columnas. Si los datos estuvieran en tablas, siempre podría pensar en usar sp_rename para crear la otra tabla nueva, pero esa no es realmente una opción.¿Confirmación por lotes en operación INSERT grande en SQL nativo?

Si realizo una única operación INSERT, el proceso utiliza una gran cantidad de espacio de registro de transacciones, normalmente archivándolo y generando muchos problemas con los DBA. (Y sí, este es probablemente un trabajo que los DBA deben manejar/design/architect)

Puedo usar SSIS y transmitir los datos a la tabla de destino con confirmaciones por lotes (pero esto requiere que los datos se transmitan a través de la red , ya que no podemos ejecutar paquetes de SSIS en el servidor).

Cualquier otra cosa que no sea dividir el proceso en múltiples operaciones INSERT utilizando algún tipo de clave para distribuir las filas en diferentes lotes y hacer un ciclo?

+1

Si el particionado de tablas es una opción, puede dividir las inserciones por el valor de parición. Podría hacer que sea más rápido unir los subconjuntos resultantes. –

Respuesta

4

Usted puede particionar los datos e insertar sus datos en un bucle de cursor. Eso sería casi lo mismo que la inserción de lotes SSIS. Pero corre en tu servidor.

create cursor .... 
select YEAR(DateCol), MONTH(DateCol) from whatever 

while .... 
    insert into yourtable(...) 
    select * from whatever 
    where YEAR(DateCol) = year and MONTH(DateCol) = month 
end 
+0

Esta fue finalmente la estrategia que utilicé. –

1

Usted podría utilizar el comando BCP para cargar los datos y utilizar el parámetro de tamaño de lote

http://msdn.microsoft.com/en-us/library/ms162802.aspx

proceso de dos pasos

  • BCP OUT datos de Vistas en archivos de texto
  • datos de BCP IN de archivos de texto en tablas con el parámetro de tamaño de lote
+0

13 segundos! * sacude el puño pequeño * –

+0

¿Cómo obtuviste la parte de los segundos? Solo me muestra los minutos –

+1

@Raj: desplace el cursor sobre las marcas de tiempo relativas para obtener las marcas de tiempo reales con los segundos –

1

Esto parece un trabajo para bien ol 'BCP.

2

No hay polvo de duende, lo sabes.

Sin conocer los detalles sobre el esquema real que se está transfiriendo, una solución genérica sería exactamente como usted la describe: divida el procesamiento en varias inserciones y realice un seguimiento de la (s) clave (s). Esta es una especie de pseudo-código T-SQL:

create table currentKeys (table sysname not null primary key, key sql_variant not null); 
go 

declare @keysInserted table (key sql_variant); 
declare @key sql_variant; 
begin transaction 
do while (1=1) 
begin 
    select @key = key from currentKeys where table = '<target>'; 
    insert into <target> (...) 
    output inserted.key into @keysInserted (key) 
    select top (<batchsize>) ... from <source> 
    where key > @key 
    order by key; 

    if (0 = @@rowcount) 
     break; 

    update currentKeys 
    set key = (select max(key) from @keysInserted) 
    where table = '<target>'; 
    commit; 
    delete from @keysInserted; 
    set @key = null; 
    begin transaction; 
end 
commit 

Sería complicarse más si desea permitir lotes paralelos y una partición de las llaves.

+3

En realidad, las balas de plata molidas son excelentes polvo de duendes –

+1

ty mr Kelley, lo tendré en cuenta en mi próximo proyecto;) –

5

¿Tiene la vista CUALQUIER tipo de identificador único/clave candidata? Si es así, podría seleccionar aquellas filas en una mesa de trabajo usando:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView; 

(Si tiene sentido, tal vez poner esta tabla en una base de datos diferente, tal vez con el modelo de recuperación simple, para evitar que la actividad del registro de interferir con su base de datos primaria. Esto debería generar mucho menos registro de todos modos, y puede liberar el espacio en la otra base de datos antes de reanudar, en caso de que el problema sea que no hay espacio suficiente en el disco.)

entonces usted puede hacer algo como esto, la inserción de 10.000 filas a la vez, y realizar copias de seguridad del registro en el medio:

SET NOCOUNT ON; 

DECLARE 
    @batchsize INT, 
    @ctr INT, 
    @rc INT; 

SELECT 
    @batchsize = 10000, 
    @ctr = 0; 

WHILE 1 = 1 
BEGIN 
    WITH x AS 
    (
     SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column) 
     FROM dbo.temp 
    ) 
    INSERT dbo.PrimaryTable(a, b, c, etc.) 
     SELECT v.a, v.b, v.c, etc. 
     FROM x 
     INNER JOIN dbo.HugeView AS v 
     ON v.key_column = x.key_column 
     WHERE x.rn > @batchsize * @ctr 
     AND x.rn <= @batchsize * (@ctr + 1); 

    IF @@ROWCOUNT = 0 
     BREAK; 

    BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT; 

    SET @ctr = @ctr + 1; 
END 

Eso es todo de la parte superior de la cabeza, por lo que no corte/pegar/ejecutar, pero creo que la idea general está ahí.

Tenga en cuenta que si está realizando copias de seguridad periódicas de bases de datos y registros, es probable que desee completar un lleno para iniciar nuevamente su cadena de registro.

+0

Eso es gracioso, porque antes de construir la mayor parte de este sistema en el servidor y todavía estaba usando mi cuadro para desarrollar, ya había dividido categorías de tablas para objetivos de recuperación y las puse en esquemas separados con la esperanza de que los DBA me dieran bases de datos múltiples, pero no era ... –

+0

Y, por desgracia, las claves están en más de 50 bytes de columnas compuestas, supongo que es hora de hacerlo ... –

+1

@ AaronBertrand Sé que esto es viejo, pero acabo de utilizar esto en una prueba. La teoría funciona bien, solo falta 'SET @ctr = @ctr + 1' en el ciclo para incrementar el contador del número de lote. Espero que no te importe, lo he editado (después de probar los resultados). – Bridge

2

Sé que esto es un viejo hilo, pero he hecho una versión genérica de la solución del cursor de Arthur:

--Split a batch up into chunks using a cursor. 
--This method can be used for most any large table with some modifications 
--It could also be refined further with an @Day variable (for example) 

DECLARE @Year INT 
DECLARE @Month INT 

DECLARE BatchingCursor CURSOR FOR 
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>) 
FROM <Sometable>; 


OPEN BatchingCursor; 
FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
WHILE @@FETCH_STATUS = 0 
BEGIN 

--All logic goes in here 
--Any select statements from <Sometable> need to be suffixed with: 
--WHERE Year(<SomeDateField>)[email protected] AND Month(<SomeDateField>)[email protected] 


    FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
END; 
CLOSE BatchingCursor; 
DEALLOCATE BatchingCursor; 
GO 

Esta resuelto el problema de las cargas de nuestros grandes mesas.

Cuestiones relacionadas