【问题标题】:Update a Column When Inserting a Record in SQL Server在 SQL Server 中插入记录时更新列
【发布时间】:2018-03-09 21:10:05
【问题描述】:

我有一个表 Conservation_Dev 包含这些列(其他 30 个):

  • I3_IDENTITY - 一个BIGINT 和一个唯一键
  • STATE - 2 个字母的美国州缩写
  • ZONE - 当我想存储这条记录的时区时

我还有一张表TimeZoneCodes 将美国各州映射到时区(忘记一些州位于多个时区的事实):

  • state_code - 该州的 2 个字母缩写
  • time_zone - 带有时区(EST、CST 等)的文本

我在没有时区数据的情况下将数据加载到Conservation_Dev,这是我无法控制的。我想创建一个更新记录的插入后触发器。对以前的线程进行了一些研究,我得出了以下结论:

CREATE TRIGGER [dbo].[PopulateTimeZoneBasedOnUSState]
ON [dbo].[Conservation_Dev]
AFTER INSERT
AS 
BEGIN   
    UPDATE [dbo].[Conservation_Dev]
    SET [ZONE] = (SELECT [time_zone] 
                  FROM [dbo].[TimeZoneCodes] Z 
                  WHERE Z.[state_code] = i.[STATE])
    FROM Inserted i
    WHERE [I3_IDENTITY] = i.[I3_IDENTITY]
END

我得到一个错误,但是:

不明确的列名“I3_IDENTITY”

另外,这是正确的方法吗?如果通过 SSIS 导入包一次加载的数据是 5 或 10,000 条记录,这会是个问题吗?

【问题讨论】:

  • 为什么不使用 SSIS 中的查找组件将时区添加为列,以便插入数据?触发器并不总是解决这个问题的最佳答案,因为它们对于以后维护代码的人来说不容易看到,数据似乎不知从何而来。
  • 我无权访问 SSIS 包。我无法控制该过程。
  • 我认为你是对的......
  • 如果您担心每次插入时触发器的开销,为什么不从下面获取正确结果的 SQL 并将其放入过程中。编辑它以仅更新时区为空的行,然后您可以按计划在作业中运行该过程。这将减少在每一行上触发的触发器的开销,但这意味着插入后不会立即更新行。另请注意,如果您没有创建触发器的权限,您可能没有权限来创建过程和/或作业。
  • 区域列的更新需要在插入记录时进行。由于不属于这个论坛的不同原因,我需要使用触发器。我无法改变其他任何事情。

标签: sql-server


【解决方案1】:

试试:

CREATE TRIGGER [dbo].[PopulateTimeZoneBasedOnUSState]
ON [dbo].[Conservation_Dev]
AFTER INSERT
AS 
BEGIN   
    UPDATE A
    SET A.[ZONE] = Z.[time_zone]
    FROM [dbo].[Conservation_Dev] as A
    INNER JOIN Inserted as i
    ON A.[I3_IDENTITY] = i.[I3_IDENTITY]
    INNER JOIN [dbo].[TimeZoneCodes] as Z 
    ON Z.[state_code] = i.[STATE]
END

【讨论】:

  • 这没有给我任何错误,而且看起来不错。不幸的是,我刚刚发现由于权限而无法创建触发器!我会尝试解决这个问题,但我会选择你的作为答案。如果触发器运行时有任何问题,我会通知您。现在,您是否知道一次插入 5 到 10 K 条记录时存在任何潜在问题?
  • @Amarundo 问题在于,对于您插入的每一条记录,您将连接 5k 到 10k 条记录。然后你搜索它以插入你的值。您已经拥有的更新将在 5k 到 10k 记录中搜索单个值,然后将其插入。 (应该会更快)连接很昂贵,尤其是当您从 SSIS 接收记录时,您要连接 5k 到 10k 条记录 5k 到 10k 次。
【解决方案2】:

我的投票是将此更新移动到 SSIS 包中,以便将数据插入全部集中在一个位置。然后我可能会转到加载数据后运行的存储过程。每次插入都会触发一个触发器。我认为基于表的查询/更新会有更好的性能。从故障排除的角度来看,触发器可能很难找到,但如果它们有据可查,那么它可能不是什么大问题。插入记录后,触发器将允许填充区域。

您创建的触发器的问题是因为 SQL Server 不知道您在 WHERE 子句的第一部分中指的是哪个 I3_IDENTITY。这应该可以解决它:

CREATE TRIGGER [dbo].[PopulateTimeZoneBasedOnUSState]
ON [dbo].[Conservation_Dev]
AFTER INSERT
AS 
BEGIN   
    UPDATE [dbo].[Conservation_Dev]
    SET [ZONE] = (SELECT TOP 1 [time_zone] FROM [dbo].[TimeZoneCodes] Z WHERE Z.[state_code] = i.[STATE])
    FROM Inserted i
    WHERE [dbo].[Conservation_Dev].[I3_IDENTITY] = i.[I3_IDENTITY]
END

这将是一个表级更新,将在一次扫描中更新所有 time_zones。在初始插入完成后,我会在存储过程中使用类似的东西。

CREATE PROCEDURE dbo.UpdateAllZones   
AS 
 Update dbo.Conservation_Dev
 SET Zone = Time_Zone
 From dbo.Conservation_Dev c INNER JOIN dbo.TimeZoneCodes z ON c.state_code = z.state_code
Go

执行为 EXEC dbo.UpdateAllZones

【讨论】:

  • 我无权了解数据的加载方式。这是由第三方完成的。哪种解决方案的性能更高效?上面有 JOIN 的那个还是这个?
  • 两个都试试看执行计划。您的原始更新是最快的。它将从 5k 条记录中选择并查找值并插入该值。通过使用 JOIN,您将在每次插入时加入 5k 到 10k 条记录,这将使您可以进行更多搜索。 JOIN 可能很昂贵。
  • 我无法创建 SP。我只拥有与这张桌子相关的东西。数据库中还有许多其他我无法控制或维护的东西。我唯一的选择是触发器。问题是使用 JOIN 版本还是 SELECT 版本。
  • 使用选择版本
猜你喜欢
  • 1970-01-01
  • 2020-11-21
  • 2013-12-06
  • 2016-03-14
  • 2015-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
相关资源
最近更新 更多