为每一行做多个插入

时间:2013-02-13 10:53:51

标签: sql tsql insert

简而言之,我的问题是如何在没有游标(伪代码)的t-sql 2012中执行以下操作:

for each r in input_list:

    insert into t1(...) ...

    if (r.field1 is not null)
        insert into tA(...) (...@@identity ...   r.field1) ... 

    else if (r.field2 is not null)
        insert into tB(...) (...@@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

我需要编写一个存储过程,它接受一个“逻辑对象”列表(可以表示文件或目录),并且必须将它们添加到DB中,即它必须为每个逻辑对象创建1)一行在obj中,以及2)文件OR dir中的一行(取决于逻辑对象是代表文件还是目录)。

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

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独有的功能(例如序列)。

2 个答案:

答案 0 :(得分: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并且不要过早过滤,以便行号保持匹配非常重要。

答案 1 :(得分: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
相关问题