SQL Server 触发器:子查询返回超过 1 个值
Posted
技术标签:
【中文标题】SQL Server 触发器:子查询返回超过 1 个值【英文标题】:SQL Server trigger : Subquery returned more than 1 value 【发布时间】:2022-01-17 01:16:30 【问题描述】:我使用触发器尝试使用以下语句更新另一个表中的记录,但出现错误
子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。声明已终止。
这是我使用的代码
ALTER TRIGGER [dbo].[WO-A]
ON [dbo].[WORKORDERS]
AFTER UPDATE, INSERT,
AS
IF ((SELECT ROUTING FROM INSERTED) LIKE 'Assy'
or (SELECT ROUTING FROM INSERTED) LIKE 'L&P'
or (SELECT ROUTING FROM INSERTED) LIKE 'ASSY FD'
or (SELECT ROUTING FROM INSERTED) LIKE 'ASSY BD'
or (SELECT ROUTING FROM INSERTED) LIKE 'RGA'
)
BEGIN
set nocount on
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE T1
SET
[Assembly Notes]=T2.[Assembly Notes],
[Assy SO Confirm]=T2.[ASSEMBLY LOOKUP C],
[CALC COLOR]=T2.[COLOR],
[PILOT SIZE]=T2.[PILOT SIZE2],
[NO OF HOLES]=T2.[BOLT HOLES2],
[C/S OD]=T2.[C/S OD2],
[DISC OD]=T2.[DISC OD],
[C/S ANGLE2]=T2.[C/S ANGLE2],
[BH SIZE2]=T2.[BH SIZE2],
[SHORT WO PN]=t2.[CALC STOCK NO],
[CALC OFF-SET]=T2.[CALC OFF-SET],
[RUN OUT LAT]=T2.[RUN OUT LAT],
[RUN OUT RAD]=T2.[RUN OUT RAD],
[BOM SECONDRY DISC]=T2.[BOM - SECONDARY DISC],
[BH SPEC]=T2.[BH SPEC],
[BH CIRCLE2]=T2.[BH CIRCLE2],
[DISC THICKNESS2]=T2.[DISC THICKNESS],
[MRP BOM PARTS 1]=T2.[WHL BOM PART 1 PN],
[MRP BOM PARTS 2]=T2.[WHL BOM PART 2 PN],
[MRP BOM PARTS 3]=T2.[WHL BOM PART 3 PN],
[MRP BOM PARTS 4]=T2.[WHL BOM PART 4 PN],
--[ASSY PN-S]=T2.[CALC STOCK NO],
[SHORT WO PN2]=T2.[CALC STOCK NO],
[WO SALES DESCRIPTION] = T2.[Description for Sales],
--[Assy SO Confirm]=T2.[Assembly Lookup C],
[CUSTOMER PN]=T2.[CUSTOMER PN],
[MRP Wheel]=T2.[BOM WHEEL PN],
[TIRE PN]=T2.[BOM tire],
[TIRE SIZE]=T2.[TIRE SIZE],
[TIRE SUPPLIER]=T22.[Manufacturer],
[TIRE QUANTITY]=CASE WHEN T1.[TIRE PN]!='' THEN T1.[QUANTITY]
WHEN T1.[TIRE PN] IS NOT NULL THEN T1.[QUANTITY]
ELSE ''
END,
[RIM PN (MRP)]=t2.[BOM - RIM PN],
[WO Popularity]=t2.[POPULARITY],
--[RIM QUANTITY]=t2.[q
[WEIGHT]=T2.[unitWeight],--?? CHECK WETHER RIGHT COLUMN
--[ASSY DESC FOR LABEL]
SIZE=T21.[SIZE],
[MRP BLK DISC]=T21.[BLK DISC PN],
[WHEEL PN 1]=NULL,
[WHEEL PN 1-S]=NULL,
[ASSY PN-S]=NULL,
[MO ASSY PN]=null
FROM WORKORDERS T1
INNER JOIN inserted i ON T1.[WORK ORDER #] = i.[WORK ORDER #]
CROSS APPLY (SELECT TOP 1 *
FROM [ASSEMBLIES] T2
WHERE t1.[ASSY PN] = T2.[ASSEMBLY LOOKUP V 1]) T2
CROSS APPLY (SELECT TOP 1 *
FROM [TIRES] T22
WHERE T22.[Tire Lookup H] = T2.[BOM Tire] ) T22
CROSS APPLY (SELECT TOP 1 *
FROM [WHEELS] T21
WHERE T2.[BOM WHEEL] = T21.[WHEEL LOOKUP V] ) T21
END
【问题讨论】:
如果您一次更新/插入多于 1 行,您将从第一次 IF 检查中得到该错误((SELECT ROUTING FROM INSERTED) LIKE 'Assy' .. etc)。始终以适用于多行的方式编写触发器。 如果插入了两行或多行,您的 If 类将获得不止一行并且会失败(我相信) 您的触发器有 MAJOR 缺陷,因为您似乎认为它会每行调用一次 - 那是不是 b> 情况。触发器将每条语句触发一次,因此如果您的INSERT
语句导致此触发器触发插入 25 行,您将触发触发器一次,但随后Inserted
伪表将包含 25 行。您的代码将在这 25 行中选择哪一行? SELECT ROUTING FROM INSERTED
- 它是不确定的,您将获得任意行,您将忽略所有其他行。您需要重写触发器以考虑到这一点!
谢谢大家!@marc_s 我该如何更新?
【参考方案1】:
这种方式应该适用于多行插入/更新,同时仅限于那些 ROUTING 值。
请注意,您的交叉应用确实需要在其中包含 order by 子句,否则您将抓取随机行并可能会得到意想不到的结果(也可能值得将每个中的 * 更改为您需要的列列表,以便正确可以使用索引)。
ALTER TRIGGER [dbo].[WO-A]
ON [dbo].[WORKORDERS]
AFTER UPDATE, INSERT,
AS
BEGIN
SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL() > 1
RETURN
ELSE
UPDATE T1
SET
[Assembly Notes]=T2.[Assembly Notes],
[Assy SO Confirm]=T2.[ASSEMBLY LOOKUP C],
[CALC COLOR]=T2.[COLOR],
[PILOT SIZE]=T2.[PILOT SIZE2],
[NO OF HOLES]=T2.[BOLT HOLES2],
[C/S OD]=T2.[C/S OD2],
[DISC OD]=T2.[DISC OD],
[C/S ANGLE2]=T2.[C/S ANGLE2],
[BH SIZE2]=T2.[BH SIZE2],
[SHORT WO PN]=t2.[CALC STOCK NO],
[CALC OFF-SET]=T2.[CALC OFF-SET],
[RUN OUT LAT]=T2.[RUN OUT LAT],
[RUN OUT RAD]=T2.[RUN OUT RAD],
[BOM SECONDRY DISC]=T2.[BOM - SECONDARY DISC],
[BH SPEC]=T2.[BH SPEC],
[BH CIRCLE2]=T2.[BH CIRCLE2],
[DISC THICKNESS2]=T2.[DISC THICKNESS],
[MRP BOM PARTS 1]=T2.[WHL BOM PART 1 PN],
[MRP BOM PARTS 2]=T2.[WHL BOM PART 2 PN],
[MRP BOM PARTS 3]=T2.[WHL BOM PART 3 PN],
[MRP BOM PARTS 4]=T2.[WHL BOM PART 4 PN],
--[ASSY PN-S]=T2.[CALC STOCK NO],
[SHORT WO PN2]=T2.[CALC STOCK NO],
[WO SALES DESCRIPTION] = T2.[Description for Sales],
--[Assy SO Confirm]=T2.[Assembly Lookup C],
[CUSTOMER PN]=T2.[CUSTOMER PN],
[MRP Wheel]=T2.[BOM WHEEL PN],
[TIRE PN]=T2.[BOM tire],
[TIRE SIZE]=T2.[TIRE SIZE],
[TIRE SUPPLIER]=T22.[Manufacturer],
[TIRE QUANTITY]=CASE WHEN T1.[TIRE PN]!='' THEN T1.[QUANTITY]
WHEN T1.[TIRE PN] IS NOT NULL THEN T1.[QUANTITY]
ELSE ''
END,
[RIM PN (MRP)]=t2.[BOM - RIM PN],
[WO Popularity]=t2.[POPULARITY],
--[RIM QUANTITY]=t2.[q
[WEIGHT]=T2.[unitWeight],--?? CHECK WETHER RIGHT COLUMN
--[ASSY DESC FOR LABEL]
SIZE=T21.[SIZE],
[MRP BLK DISC]=T21.[BLK DISC PN],
[WHEEL PN 1]=NULL,
[WHEEL PN 1-S]=NULL,
[ASSY PN-S]=NULL,
[MO ASSY PN]=null
FROM WORKORDERS T1
INNER JOIN inserted i ON T1.[WORK ORDER #] = i.[WORK ORDER #]
CROSS APPLY (SELECT TOP 1 *
FROM [ASSEMBLIES] T2
WHERE t1.[ASSY PN] = T2.[ASSEMBLY LOOKUP V 1]) T2
CROSS APPLY (SELECT TOP 1 *
FROM [TIRES] T22
WHERE T22.[Tire Lookup H] = T2.[BOM Tire] ) T22
CROSS APPLY (SELECT TOP 1 *
FROM [WHEELS] T21
WHERE T2.[BOM WHEEL] = T21.[WHEEL LOOKUP V] ) T21
WHERE i.ROUTING IN ('Assy', 'L&P', 'ASSY FD', 'ASSY BD', 'RGA')
END
【讨论】:
【参考方案2】:如果您在触发器中使用来自INSERTED
或DELETED
表的子查询,则最好使用支持多行操作的语法。
例如你在这里使用IF ((SELECT ROUTING FROM INSERTED) LIKE 'Assy'...
如果此子查询 (SELECT ROUTING FROM INSERTED LIKE 'Assy'
) 返回多于一行,则 if
操作将失败。在这种情况下,您一次只能插入或更新一行。所以你不能使用这个查询,例如:
UPDATE [dbo].[WORKORDERS]
SET ROUTING='L'
WHERE ... --Queries that return more than one row
【讨论】:
以上是关于SQL Server 触发器:子查询返回超过 1 个值的主要内容,如果未能解决你的问题,请参考以下文章
我无法在 Microsoft SQL Server 2014 中创建正确的触发器
sql server触发器 子查询返回值不止一个 如何解决?