Question

J'ai quelques tables volumineuses (188 m et 144 m de lignes) que je dois renseigner à partir de vues, mais chaque vue contient quelques centaines de millions de lignes (regroupant des données modélisées de manière pseudo-dimensionnelle dans un formulaire à plat). Les clés de chaque table contiennent plus de 50 octets composites de colonnes. Si les données étaient dans des tables, je pourrais toujours penser à utiliser sp_rename pour créer l’autre nouvelle table, mais ce n’est pas vraiment une option.

Si je ne fais qu'une seule opération INSERT, le processus utilise une grande quantité d’espace de journal des transactions. Il est généralement archivé et archivé, ce qui entraîne de nombreux problèmes avec les administrateurs de base de données. (Et oui, c’est probablement un travail que les administrateurs de base de données devraient gérer / concevoir / architecte)

Je peux utiliser SSIS et diffuser les données dans la table de destination avec des validations par lot (mais cela nécessite la transmission des données sur le réseau, car nous ne sommes pas autorisés à exécuter les packages SSIS sur le serveur).

Autre chose que de diviser le processus en plusieurs opérations INSERT en utilisant une sorte de clé pour répartir les lignes en différents lots et en effectuant une boucle?

Était-ce utile?

La solution

Vous pouvez partitionner vos données et les insérer dans une boucle de curseur. Ce serait presque la même chose que SSIS batchinserting. Mais fonctionne sur votre serveur.

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

Autres conseils

La vue comporte-t-elle AUCUN type d'identifiant unique / clé candidate? Si tel est le cas, vous pouvez sélectionner ces lignes dans une table de travail en utilisant:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

(Si cela vous semble judicieux, placez peut-être cette table dans une base de données différente, avec éventuellement un modèle de récupération SIMPLE, pour éviter que l'activité du journal n'interfère avec votre base de données principale. Cela devrait générer beaucoup moins de journaux de toute façon, et vous pourrez libérer le espace dans l’autre base de données avant de reprendre, au cas où vous auriez un espace disque insuffisant.)

Vous pouvez ensuite faire quelque chose comme ceci, en insérant 10 000 lignes à la fois et en sauvegardant le journal 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

C'est tout ce que je pense, alors ne coupez pas, ne collez pas et ne courez pas, mais je pense que l'idée générale est là.

Notez que si vous effectuez des sauvegardes régulières de la base de données et des journaux, vous souhaiterez probablement en reprendre un complet pour redémarrer votre chaîne de journaux.

Je sais que c'est un ancien fil de discussion, mais j'ai créé une version générique de la solution de curseur d'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

Cela a résolu le problème des charges de nos grandes tables.

Il n'y a pas de poussière de lutin, vous le savez.

Sans connaître les détails du schéma en cours de transfert, une solution générique correspondrait exactement à ce que vous décrivez: divisez le traitement en plusieurs insertions et gardez une trace de la ou des clés. C'est en quelque sorte un pseudo-code 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

Cela deviendrait plus compliqué si vous souhaitez autoriser les lots parallèles et partitionner les clés.

Vous pouvez utiliser la commande BCP pour charger les données et utiliser le paramètre Taille du lot

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

Processus en deux étapes

  • Données BCP OUT des vues dans les fichiers texte
  • Données BCP IN de fichiers texte en tableaux avec le paramètre de taille de lot

Cela ressemble à un travail de bon vieux BCP .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top