2013-02-13 64 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