Pregunta

Tengo un par de tablas grandes (filas de 188m y 144m) que necesito para rellenar desde las vistas, pero cada vista contiene unos cientos de millones de filas (agrupando datos modelados pseudo-dimensionalmente en una forma plana). Las claves en cada tabla son más de 50 bytes compuestos de columnas. Si los datos estaban en tablas, siempre podría pensar en usar sp_rename para hacer la otra tabla nueva, pero esa no es realmente una opción.

Si hago una sola operación de INSERTAR, el proceso utiliza una gran cantidad de espacio de registro de transacciones, normalmente archivándolo y provocando un montón de problemas con los DBA. (Y sí, este es probablemente un trabajo que deben manejar los DBA / 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 se nos permite ejecutar paquetes 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 bucle?

¿Fue útil?

Solución

Podría dividir sus datos e insertar sus datos en un bucle de cursor. Eso sería casi lo mismo que SSIS batchinserting. Pero se ejecuta en su 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

Otros consejos

¿La vista tiene CUALQUIER tipo de identificador único / clave candidata? Si es así, puede seleccionar esas filas en una tabla de trabajo usando:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

(Si tiene sentido, quizás coloque esta tabla en una base de datos diferente, tal vez con un modelo de recuperación SIMPLE, para evitar que la actividad del registro interfiera con su base de datos principal. 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 tiene suficiente espacio en el disco.)

Luego puedes hacer algo como esto, insertando 10,000 filas a la vez, y haciendo una copia de seguridad del registro entre:

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

Todo está fuera de mi cabeza, así que no cortes / pegar / correr, pero creo que la idea general está ahí.

Tenga en cuenta que si realiza copias de seguridad periódicas de la base de datos y el registro, probablemente querrá aprovechar al máximo para volver a iniciar su cadena de registro.

Sé que este es un tema antiguo, pero hice una versión genérica de la solución de 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>)=@Year AND Month(<SomeDateField>)=@Month   


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

Esto resolvió el problema en las cargas de nuestras tablas grandes.

No hay polvo de duendecillos, lo sabes.

Sin conocer los detalles específicos sobre el esquema real que se está transfiriendo, una solución genérica sería exactamente como la describe: divida el procesamiento en varias inserciones y realice un seguimiento de la (s) clave (s). Este es un tipo 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

Se complicaría más si quisiera permitir lotes paralelos y particionar las claves.

Puede usar el comando BCP para cargar los datos y usar el parámetro Tamaño de lote

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

Proceso de dos pasos

  • Los datos BCP OUT de las vistas a los archivos de texto
  • BCP IN data de archivos de texto en tablas con parámetro de tamaño de lote

Esto parece ser un trabajo para el bien ' BCP .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top