【问题标题】:SQL Insert Using Records From Another Insert使用来自另一个插入的记录的 SQL 插入
【发布时间】:2015-03-20 15:12:15
【问题描述】:

我有3张表,如下:

tblAgentVisit (VisitID auto-increments)
VisitID (PK) | StatusID | A bunch of other columns
--------------------------------------------------
1            | 1        | etc.
2            | 1        | etc.

tblAgentVisitAgents
VisitID | AgtID | Prime
-----------------------
1       | 8507  | 1
2       | 56    | 1

tblAgentVisitLoad
AgtID
-----
2077
3068
432

每个季度,我们都会挑选前 20% 的代理,并将他们的 ID 加载到 tblAgentVisitLoad

我需要为表中的每个代理创建一个新的、唯一的访问。我需要 INSERT 我的 StatusID 和其他列到 tblAgentVisit,获取自动递增的 VisitID(使用 OUTPUT?),然后将 INSERT VisitID、AgtID 和 Prime = 1 放入 tblAgentVisitAgents。同样,每个代理的每次访问都必须是唯一的。

理想情况下,这就是我完成后表格的外观:

tblAgentVisit (VisitID auto-increments)
VisitID (PK) | StatusID | A bunch of other columns
--------------------------------------------------
1            | 1        | etc.
2            | 1        | etc.
3            | 1        | etc.
4            | 1        | etc.
5            | 1        | etc.

tblAgentVisitAgents
VisitID | AgtID | Prime
-----------------------
1       | 8507  | 1
2       | 56    | 1
3       | 2077  | 1
4       | 3068  | 1
5       | 432   | 1

有人对在 MS SQL Server 2005 中执行此操作有什么建议吗?我完全被难住了。

【问题讨论】:

  • 如果您可以将 DateCreated 字段添加到 tblAgentVisit,您可以根据该字段获取当前季度的记录并将这些记录插入 tblAgentVisitAgents。
  • 问题是这可能会或可能不会在季度的第一天运行,并且其他单数记录将随机添加。我想。我们可能会改变它的工作方式,所以我会记住这一点。谢谢!
  • 你在正确的轨道上。使用INSERT 上的OUTPUT 子句将新分配的标识值放入临时表中,然后继续下一个任务。
  • 我想我只是不明白如何将 tblAgentVisitLoad 中的每条记录作为自己的条目进行迭代。由于每个代理都需要一个唯一的 VisitID,因此它们必须在同一个块中运行,然后移动到下一条记录,对吗?

标签: sql sql-server tsql sql-server-2005


【解决方案1】:

我有两个潜在的解决方案给你。第一个为稍微简单的方法交换了一些空间,第二个更复杂但总体上可能更好。

使用OUTPUT 也是一种替代方法,但它需要将分配写入临时表(因为接收表不能有约束),然后从那里写入tblAgentVisitAgents。这是另一个 SO 问题,详细说明了如何做到这一点:

How can I INSERT data into two tables simultaneously in SQL Server?

解决方案 1:为第一个代理分配使用临时字段。

首先,在tblAgentVisit 中添加一列(例如FirstAgentID)以在创建每次访问时临时存储分配的代理:

ALTER TABLE tblAgentVisit ADD FirstAgentID int NULL;

如果您使用的是 MSSQL 2008 或更高版本,您可以将此列设为 SPARSE,这样任何 NULL 记录都不会占用空间,但这在 2005 年不可用。不过,希望额外的 NULL int 列不会成为问题给你。

接下来,创建新的访问记录,临时存储您打算分配给每次访问的代理:

INSERT INTO tblAgentVisit(Status, ..., FirstAgentID)
SELECT ..., AgtId FROM tblAgentVisitLoad;

现在,使用新访问记录(及其临时FirstAgentID)创建附属记录,为每次新访问分配适当的代理:

INSERT INTO tblAgentVisitAgents(VisitID, AgtID, Prime)
SELECT VisitID, FirstAgentID, 1
FROM tblAgentVisit
WHERE FirstAgentID IS NOT NULL;

最后,将临时字段清空,以免造成任何潜在的混淆或数据重复:

UPDATE tblAgentVisit
SET FirstAgentID=NULL
WHERE FirstAgentID IS NOT NULL;

解决方案 #2:使用 CTE 和 ROW_NUMBER() 按顺序位置将新访问分配给代理。

我相信您也可以使用 CTE 和 ROW_NUMBER() 在两个语句中执行此操作,而无需上述临时字段:

INSERT INTO tblAgentVisit(Status, ...) blah;

WITH a AS (
   SELECT AgtId, ROW_NUMBER() OVER(ORDER BY AgtId ASC) AS row
   FROM tblAgentVisitLoad
), b AS (
   SELECT VisitID, ROW_NUMBER() OVER(ORDER BY VisitID ASC) AS row
   FROM tblAgentVisit
   WHERE NOT EXISTS(SELECT NULL FROM tblAgentVisitAgents c WHERE
      c.VisitID = tblAgentVisit.VisitID
)
INSERT INTO tblAgentVisitAgents(VisitID, AgtID, Prime)
SELECT b.VisitID, a.AgtID, 1
FROM a, b
WHERE a.row = b.row;

这将利用这样一个事实,即在 INSERT 之后没有分配代理的唯一访问是新访问。如果在其他情况下访问可能没有任何对应的tblAgentVisitAgents 记录,则需要以某种方式过滤 CTE b 以仅包含新创建的访问。

【讨论】:

  • 这些都是很好的、可行的解决方案。谢谢!
猜你喜欢
  • 1970-01-01
  • 2021-01-18
  • 2011-02-15
  • 2016-01-17
  • 1970-01-01
  • 1970-01-01
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多