2013-02-13 95 views
0

简单地说,我的问题是如何做到以下在T-SQL 2012,没有游标(伪代码):做多个插入的每一行

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) ... 

长的问题:

假设我有以下3个表格模拟了对象可以是文件或目录的事实。

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 

我需要写一个存储过程,其以“逻辑对象”的列表(可以表示文件或显示目录),并必须将它们添加到数据库,即它必须创造,每个逻辑对象, 1)obj中的一行,以及2)文件或目录中的一行(取决于逻辑对象是代表文件还是目录)。

为了编写这个存储过程,我创建了一个表示逻辑对象的表参数。这必须是能够代表两者一文件和目录,所以它必须包含文件和目录的(逻辑)字段的合并时,如下所示:

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

我的存储过程与表值定义现在

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

as 
begin 
... 
end 

过程体,我认为我需要做类似的信息(伪代码):参数如下

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) 

我的问题:如何做到这一点没有游标?如果需要的话,可以使用t-sql 2012特有的功能(如序列)。

回答

1

您可以使用output clause从第一基于集合的插入捕捉与标识值的多个行。然后,您可以使用ROW_NUMBER()子句将这些捕获的输出值与原始@lo变量中的行相关联。

这将是这样的:

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 

它全面查询@lo在底部的两个刀片NumberedObjects,而不是过滤太早,使行数不断向上的匹配是很重要的。

0

为什么不把它作为三个插入?

事情是这样的:

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

我认为这不起作用,因为第一个“insert into obj”会插入一堆行,每行都带有新的@@标识。那么当您选择@@身份时,您只使用最后创建的身份。 – seguso 2013-02-13 12:26:59