2013-02-13 3 views
0

En bref, ma question est de savoir comment faire ce qui suit dans t-sql 2012, sans curseurs (pseudo-code):faire plusieurs inserts pour chaque ligne

for each r in input_list: 

    insert into t1(...) ... 

    if (r.field1 is not null) 
     insert into tA(...) ([email protected]@identity ... r.field1) ... 

    else if (r.field2 is not null) 
     insert into tB(...) ([email protected]@identity... r.field2) ... 

longue question:

Supposons que j'ai les 3 tables suivantes, modélisant le fait qu'un objet peut être un fichier ou un répertoire.

obj(id int, creation_date datetime) -- all objects have a creation date. 
file(id int, id_obj int, path nvarchar(max)) -- id_obj is a foreign key to obj 
dir(id int, id_obj int, path nvarchar(max), shared bit) -- id_obj is a foreign key to obj 

J'ai besoin d'écrire un proc stocké qui prend une liste des « objets logiques » (qui peut représenter des fichiers ou des répertoires) et doit les ajouter à la base de données, à savoir qu'il doit créer, pour chaque objet logique, 1) une ligne dans obj, et 2) une ligne dans le fichier OR dir (selon que l'objet logique représente un fichier ou un répertoire).

Pour écrire ce proc stocké, j'ai créé un paramètre de table représentant l'objet logique. Celle-ci doit être en mesure de représenter à la fois un fichier et un répertoire, il doit contenir une fusion des champs de fichier et dir (logiques), comme suit:

create type logicalObj as table(
           dirPath nvarchar(max) null, 
           dirShared bit null, 
           filePath nvarchar(max) null 
           ) 

Ma procédure stockée est définie par une table-valeur paramètre comme suit:

create procedure foo 
     -- this way the user can pass a list of logical objects to the stored proc 
    @lo logicalObj readonly . 

as 
begin 
... 
end 

maintenant dans le corps de la procédure, je pense que je dois faire quelque chose comme (pseudo-code):

for each lo in @lo: 

    insert into obj(creation_date) 
     values (curdate()) 

    if lo.dirPath is not null 
     insert into dir(id_obj, path, shared) 
     values (@@identity, lo.dirPath, 1) 
    else if lo.filePath is not null 
     insert into file(id_obj, path) 
     values (@@identity, lo.dirPath) 

Ma question: comment faire sans curseurs? Il est permis d'utiliser des fonctionnalités propres à t-sql 2012 (telles que des séquences) si nécessaire. Pourquoi ne pas le faire en trois insertions? Non

Répondre

1

Vous pouvez utiliser un output clause pour capturer plusieurs lignes avec des valeurs d'identité de la première insertion basé sur un ensemble. Vous pouvez ensuite utiliser les clauses ROW_NUMBER() pour corréler ces valeurs de sortie capturées avec les lignes de la variable @lo d'origine.

Ce sera quelque chose comme:

declare @IDs table (ID int not null) 
insert into obj(creation_date) 
output inserted.id into @IDs 
select curdate() from @lo --Just makes sure there's one row per row in @lo 

;with NumberedIDs as (
    select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from @IDs 
), NumberedObjects as (
    select *,ROW_NUMBER() OVER (ORDER BY dirPath,filePath) as rn from @lo 
) 
insert into dir (id_obj, path, shared) 
select nid.ID,no.dirPath,no.dirShared 
from NumberedObjects no 
     inner join 
    NumberedIDs nid 
     on 
     no.rn = nid.rn 
where 
    no.dirPath is not null 

;with NumberedIDs as (
    select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from @IDs 
), NumberedObjects as (
    select *,ROW_NUMBER() OVER (ORDER BY dirPath,filePath) as rn from @lo 
) 
insert into file (id_obj, path) 
select nid.ID,no.filePath 
from NumberedObjects no 
     inner join 
    NumberedIDs nid 
     on 
     no.rn = nid.rn 
where 
    no.filePath is not null 

Il est important de bien @lo requête en NumberedObjects dans les deux culots et ne pas filtrer trop tôt, de sorte que les numéros de ligne continuent à correspondre.

0

Quelque chose comme ceci:

INSERT into obj(creation_date) 
SELECT curdate() FROM @lo WHERE (lo.dirPath is not null) OR (lo.dirPath is null AND lo.filePath is not null) 

insert into dir(id_obj, path, shared) 
SELECT @@identity, lo.dirPath, 1 FROM @lo WHERE lo.dirPath is not null 

insert into file(id_obj, path) 
SELECT @@identity, lo.dirPath, 1 FROM @lo lo WHERE lo.dirPath is null AND lo.filePath is not null 
+0

Je pense que cela ne fonctionnera pas, car le premier "insert into obj" insère un tas de lignes, chacune avec leur nouvelle identité @@. Lorsque vous sélectionnez @@ identity, vous utilisez uniquement la dernière identité créée. – seguso