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】:

如果您在触发器中使用来自INSERTEDDELETED 表的子查询,则最好使用支持多行操作的语法。

例如你在这里使用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触发器 子查询返回值不止一个 如何解决?

SQL server 建立的触发器子查询返回值不唯一,需要用多个返回值如何操作

sql 触发器 子查询返回的不止一个值

子查询返回超过 1 个值 - 我的触发器无法处理多个行更新

SQL 错误 ORA 01427 - 子查询返回超过 1 行的更新语句