6

假设一个模式包含以下表:

巴兹

BazID (PK, Identity)
Description

FooTemplate(一个 Baz 可能有零到多个 FooTemplates)

FooTemplateID (PK, Identity)
BazID (FK)
Description
NextGenerationDate

BarTemplate(一个 FooTemplate 可能有零到多个 BarTemplate)

BarTemplateID (PK, Identity)
FooTemplateID (FK)
Description

Foo(一个 Baz 可能有零到多个 Foo)

FooID (PK, Identity) 
BazID (FK)
Description

Bar(一个 Foo 可能有零到多个 Bar)

BarID (PK, Identity)
FooID (FK)
Description

每天将执行一个存储过程来为已超过其下一代日期的关联实体Foo生成Bar实体。Baz

这个过程的第一部分看起来有点像这样:

DECLARE @GeneratedFooIDList TABLE (INT FooID);
INSERT Foo (BazID, Description)
OUTPUT inserted.FooID INTO @GeneratedFooIDList
SELECT
  BazID
  Description
FROM
  FooTemplate
WHERE
  NextGenerationDate < GETDATE()

我的问题是我现在可以执行什么语句来生成正确的Bar实体并使它们与新创建的Foo实体正确关联?

编辑:该过程将在运行 SQL Server 2005 的服务器上执行。

EDIT2:感谢大家的帮助。在仔细考虑了这些信息后,我选择了另一种解决方案。我已将 Foo 表中的主键更改为不再是自动生成的标识列,这样可以执行对临时表的中间插入以捕获相关的 FooTemplateID 以及 FooID

4

2 回答 2

2

如果我正确理解您的架构

declare @GeneratedFooIDList table (FooID int, FooTemplateID int)
declare @Date datetime = getdate()

/*
insert Foo (BazID, Description)
    output inserted.FooID, FT.FooTemplateID into @GeneratedFooIDList
select
    FT.BazID,
    FT.Description
from FooTemplate as FT
where
    FT.NextGenerationDate < @Date
*/

merge into Foo using
(
    select *
    from FooTemplate as FT
    where
        NextGenerationDate < @Date
) as FT on 1 = 0
when not matched then
insert (BazID, Description)
values (BazID, Description)
output inserted.FooID, FT.FooTemplateID into @GeneratedFooIDList;

insert Bar (FooID, Description)
select
    G.FooID
    BT.Description
from BarTemplate as BT
    inner join @GeneratedFooIDList as G on G.FooTemplateID = BT.FooTemplateID

好吧,如果您有 SQL Server 2005,那么这将不起作用。我可以建议另一种解决方案,它取决于表中组合(BazID, Description)的唯一性。FooTemplate如果有帮助,它也可以用 date < @Date 的 fooTemplate 变量表重写。

http://sqlfiddle.com/#!3/ee576/29

declare @GeneratedFooIDList table (FooID int)
declare @Date datetime = getdate()

insert Foo (BazID, Description)
    output inserted.FooID into @GeneratedFooIDList
select
    FT.BazID,
    FT.Description
from FooTemplate as FT
where
    FT.NextGenerationDate < @Date

insert Bar (FooID, Description)
select
    G.FooID,
    BT.Description
from @GeneratedFooIDList as G
    inner join Foo as F on F.FooID = G.FooID
    inner join FooTemplate as FT on FT.BazID = F.BazID and FT.Description = F.Description
    inner join BarTemplate as BT on BT.FooTemplateID = FT.FooTemplateID
于 2012-10-23T13:01:07.423 回答
1

这是一个很大的假设,即每个 BazID 只有一个 FooTemplate,每个 FooTemplate 只有一个 BarTemplate。如果不是这种情况,则结构很难确定哪个 baz 与哪个 foo 模板一起使用,以及哪个 bartemplate 与哪个 footemplate 一起使用——您必须在描述列中折腾以确保唯一性,并且没有 PK 或 UQ你不能确定唯一性。

INSERT Bar (FooID, Description)
 select new.FooId, bt.Description
  from @GeneratedFooIDList new
   inner join Foo f
    on f.FooId = new.FooId
   inner join FooTemplate  ft  --  one to one assumption here
    on ft.BazID = f.BazID
   inner join BarTemplate bt  --  another one to one assumption here
    on bt.FooTemplateId = ft.FooTemplateId

根据@RomanPekar,如果您可以在临时表中添加 FooTemplateId,那么以后的更新会更简单,但是您是对的,输出子句仅适用于inserted并且deleted(当您开始处理新的身份列时它会变得非常繁琐)


[添加]

以下是我总结核心问题的方式:

  • 您将许多行添加到 Foo 表
  • 每行都有一个新生成的代理键(FooId)
  • 现在必须为这些新的 Foo 实体中的每一个生成零个或多个 Bar 行
  • 因此,您的每个新行都必须映射回其原始 FooTemplate 行,以便获取“源”FooTemplate 的必要 BarTemplate 行
  • 但是,Foo 表中没有任何内容可以启用此映射(例如,将 Foo 与其源 FooTemplate 链接。不能使用 BazId,因为可能有多个模板;不能——嗯,不应该——使用描述,因为不保证唯一性
  • INSERT... OUTPUT... 构造也不能提供此链接,因为它只能提取插入到 Foo 表中的列

我过去针对此类问题使用的一种解决方案是使用链接数据临时填充新表 (Foo) 中的列。在这里,我将 FooTemplateId 存储在 Foo.Description 中,基于此构建必要的 INSERT...SELECT,然后更新链接到 FooTemplate 上的 Foo 并用正确的值替换 description。它有效,但很尴尬。

基于@MikaelEriksson 不幸删除的帖子和引用链接(尤其是我如何学会停止担心和喜欢 MERGE )的替代解决方案是使用合并命令,该命令(谁知道?)可以引用插入和删除之外的列它的输出子句。使用它来用新的 FooID 和源 FooTemplateId 填充临时表,然后从那里开始工作。(没有样本数据来测试和调试,我对自己的 MERGE 技能没有足够的信心来尝试编写这个过程,但我相信它可以完成。)

于 2012-10-23T13:52:38.870 回答