【问题标题】:Trigger causes subquery returned more than 1 value触发导致子查询返回超过 1 个值
【发布时间】:2014-04-21 12:34:12
【问题描述】:

我很少使用触发器,但在更新语句上执行触发器时我似乎遇到了问题。例如,我有一个名为 Project 的表,我们允许软删除。我所说的软删除是在这个表中我们有一个名为Deleted的位字段,如果为真则表示记录已被删除,如果为假则表示它是有效记录。

现在这个表有很多关系(引用这个项目的表)。例如,一个项目可以有多个与之关联的程序。所以我有一个ProgramProject 表,其中一个程序可以有多个项目(1 对多关系,其中 1 是程序,许多是项目)。所以当我删除一个项目时(根据我上面提到的),触发器应该删除与之关联的所有项目......

但是,在某些情况下,当触发器执行时,它似乎会返回错误:

System.Data.SqlClient.SqlException was caught HResult=-2146232060 Message=Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

我是否错误地执行了这些触发器?下面是一个触发器的例子:

ALTER TRIGGER [dbo].[trDeleteProject] ON [dbo].[Project]
FOR UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    --check if the Deleted field was updated
    IF(UPDATE(Deleted))
        BEGIN
            DECLARE @WasDeleted bit
            DECLARE @ProjectID int

            --get the project that was just deleted
            SET @ProjectID = (SELECT ProjectID FROM Inserted)
            --get whether they want the record deleted (1) or not (0)
            SET @WasDeleted = (SELECT Deleted FROM Inserted)

            --did they want to delete it? (1)
            if (@WasDeleted=1)
                BEGIN
                --yes they delete the record so we remove instances...

                    --1.)Remove programs when a project is deleted
                    UPDATE ProgramProject SET Deleted=1 WHERE ProjectID = @ProjectID

                    --2.)Remove project contact roles when you delete a project
                    UPDATE ProjectContactRole SET Deleted=1 WHERE ProjectID = @ProjectID

                    --3.)Remove sub projects when you delete a project
                    --UPDATE SubProject SET Deleted=1 WHERE ProjectID = @ProjectID

                    --4.)Remove any transport system when you delete a project
                    UPDATE ProjectTransportSystem SET Deleted=1 WHERE ProjectID = @ProjectID

                    --5.)Remove any project mechanical architecture when you delete a project
                    UPDATE ProjectMechanicalArchitecture SET Deleted=1 WHERE ProjectID = @ProjectID

                    --6.)Remove any install personnel when you delete a project
                    UPDATE ProjectInstallPersonnel SET Deleted=1 WHERE ProjectID = @ProjectID
                     END
        END
END

我是否因为它是触发器而错误地执行此操作?我应该加入某个临时表而不是单个更新语句吗?

更新

我尝试使用 M Ali 的示例,但遇到了同样的问题,子查询返回超过 1 行...

这是我使用的代码:

ALTER TRIGGER [dbo].[trDeleteProject] ON [dbo].[Project]
FOR UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    SELECT * INTO #temp 
    FROM Inserted i
    WHERE i.deleted = 1

        --1.)Remove programs when a project is deleted
        UPDATE P 
        SET p.Deleted = 1 
        FROM #temp t INNER JOIN ProgramProject p ON P.ProjectID = t.ProjectID

        --2.)Remove project contact roles when you delete a project
        UPDATE Pr 
        SET pr.Deleted = 1 
        FROM #temp t INNER JOIN ProjectContactRole pr ON Pr.ProjectID = t.ProjectID

        --3.)Remove sub projects when you delete a project
        UPDATE SP 
        SET SP.Deleted = 1 
        FROM #temp t INNER JOIN SubProject sp ON SP.ProjectID = t.ProjectID

        --4.)Remove any transport system when you delete a project
        UPDATE PTS 
        SET PTS.Deleted = 1 
        FROM #temp t INNER JOIN ProjectTransportSystem PTS ON PTS.ProjectID = t.ProjectID

        --5.)Remove any project mechanical architecture when you delete a project
        UPDATE PMA 
        SET PMA.Deleted = 1 
        FROM #temp t INNER JOIN ProjectMechanicalArchitecture PMA ON PMA.ProjectID = t.ProjectID

        --6.)Remove any install personnel when you delete a project
        UPDATE PIP 
        SET PIP.Deleted = 1 
        FROM #temp t INNER JOIN ProjectInstallPersonnel PIP ON PIP.ProjectID = t.ProjectID

        --7.)Remove any install shifts when you delete a project
        UPDATE PIS
        SET PIS.Deleted = 1
        FROM #temp t INNER JOIN ProjectInstallShift PIS ON PIS.ProjectID = t.ProjectID

        --8.)Remove any install windows when you delete a project
        UPDATE PIW
        SET PIW.Deleted = 1
        FROM #temp t INNER JOIN ProjectInstallWindow PIW ON PIW.ProjectID = t.ProjectID

        --9.)Remove any project pallet type when you delete a project
        UPDATE PPT
        SET PPT.Deleted = 1
        FROM #temp t INNER JOIN ProjectPalletType PPT ON PPT.ProjectID = t.ProjectID

        --10.)Remove issues related to a project
        UPDATE I
        SET I.Deleted = 1
        FROM #temp t INNER JOIN Issue I ON I.ProjectID = t.ProjectID

        --11.)Remove measurement architecture for a project
        UPDATE PMAS
        SET PMAS.Deleted = 1
        FROM #temp t INNER JOIN ProjectMeasurementArchitectureSystem PMAS ON PMAS.ProjectID = t.ProjectID

        --12.)Remove controls architecture for a project
        UPDATE PCAS
        SET PCAS.Deleted = 1
        FROM #temp t INNER JOIN ProjectControlsArchitectureSystem PCAS ON PCAS.ProjectID = t.ProjectID                  
END

【问题讨论】:

  • 我提出的方法中没有子查询,这段代码不能产生这个错误。请显示您正在执行的查询以触发此触发器。
  • 为了调试,我会在触发器的顶部和底部放置一个打印语句。如果您收到错误但没有打印,则错误在触发器之前。如果你得到两个打印然后错误是触发之后的问题。如果您获得第一次打印,然后出现错误,则问题出在触发器中。我同意@M.Ali。除非您保留一些原始代码,否则我看不出您如何使用他提出的代码来获得该错误。
  • 我相信因为这个触发器内部的更新正在调用其他触发器,所以我也必须查看其他触发器。

标签: sql sql-server triggers


【解决方案1】:

只需将逻辑删除的行放入临时表中,然后将该临时表与每个相关表连接起来,然后更新它们表中的已删除字段。

您可以简单地使用已删除的表本身与您要更新的每个表连接,但仅在字段已删除 = 1 限制临时表中的行数或行的情况下将行插入临时表。加入一个相对较小的表会给你一些性能提升。请看下面:

ALTER TRIGGER [dbo].[trDeleteProject] ON [dbo].[Project]
FOR UPDATE
AS 
BEGIN
    SET NOCOUNT ON;
-- Get logically deleted rows into a temp table 

 SELECT * INTO #temp 
 FROM Inserted i
 WHERE i.deleted = 1

        --1.)Remove programs when a project is deleted
        UPDATE P 
        SET p.Deleted = 1 
        FROM #temp t INNER JOIN ProgramProject p ON P.ProjectID = t.ProjectID

        --2.)Remove project contact roles when you delete a project
        UPDATE Pr 
        SET pr.Deleted = 1 
        FROM #temp t INNER JOIN ProjectContactRole pr ON Pr.ProjectID = t.ProjectID

        --3.)Remove sub projects when you delete a project
        UPDATE SP 
        SET SP.Deleted = 1 
        FROM #temp t INNER JOIN SubProject sp ON SP.ProjectID = t.ProjectID

        --4.)Remove any transport system when you delete a project
        UPDATE PTS 
        SET PTS.Deleted = 1 
        FROM #temp t INNER JOIN ProjectTransportSystem PTS ON PTS.ProjectID = t.ProjectID

        --5.)Remove any project mechanical architecture when you delete a project
        UPDATE PMA 
        SET PMA.Deleted = 1 
        FROM #temp t INNER JOIN ProjectMechanicalArchitecture PMA ON PMA.ProjectID = t.ProjectID

        --6.)Remove any install personnel when you delete a project
        UPDATE PIP 
        SET PIP.Deleted = 1 
        FROM #temp t INNER JOIN ProjectInstallPersonnel PIP ON PIP.ProjectID = t.ProjectID

END

更新

既然您提到您希望一次使用一个 projectID 更新表格。要实现这一点,您需要再次创建一个 while 循环和一个临时表,并循环遍历您的临时表以按 projectid 更新每个表 projectid 中的记录,我真的认为这对于一个相当简单的任务来说是一种过度杀伤力。见下文:

ALTER TRIGGER [dbo].[trDeleteProject] ON [dbo].[Project]
FOR UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

--check if the Deleted field was updated
    DECLARE @ProjectID int

  SELECT * INTO #Temp 
  FROM Inserted 
  WHERE deleted = 1

    --did they want to delete it? (1)
    WHILE EXISTS (SELECT * FROM #Temp)
        BEGIN
              SELECT TOP 1 @ProjectID = ProjectID 
              FROM #Temp

            --1.)Remove programs when a project is deleted
            UPDATE ProgramProject SET Deleted=1 WHERE ProjectID = @ProjectID

            --2.)Remove project contact roles when you delete a project
            UPDATE ProjectContactRole SET Deleted=1 WHERE ProjectID = @ProjectID

            --3.)Remove sub projects when you delete a project
            --UPDATE SubProject SET Deleted=1 WHERE ProjectID = @ProjectID

            --4.)Remove any transport system when you delete a project
            UPDATE ProjectTransportSystem SET Deleted=1 WHERE ProjectID = @ProjectID

            --5.)Remove any project mechanical architecture when you delete a project
            UPDATE ProjectMechanicalArchitecture SET Deleted=1 WHERE ProjectID = @ProjectID

            --6.)Remove any install personnel when you delete a project
            UPDATE ProjectInstallPersonnel SET Deleted=1 WHERE ProjectID = @ProjectID

         DELETE FROM #Temp WHERE ProjectID = @ProjectID  

       END 
END

【讨论】:

  • 但我只删除一行(项目本身),然后项目本身有很多关系(您在 UPDATE 语句中看到的许多边表)......所以我不能只得到 projectID删除并更新多方关系?
  • 是的,这也是可能的,但是如果用户删除多个项目,您将需要添加某种 while 循环以从每个表中删除每个 projectid,我将临时表与它处理的每个相关表连接的方式这两种情况都是单个项目删除和多个项目删除。
  • 你当前的代码只处理一次删除,但是一旦有多次删除它就会全力以赴,你不想要一个只在特定场景下工作的代码,否则会失败:)跨度>
  • 但是我们不能限制这个:SELECT * INTO #temp FROM Inserted i WHERE i.deleted = 1 仅限于被删除的项目吗?
  • 查看我的问题更新,我仍然遇到与您的代码相同的问题。
猜你喜欢
  • 1970-01-01
  • 2022-01-17
  • 2014-03-11
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多