Question

I don't use triggers much but I seem to be having an issue when a trigger is executed on an update statement. For instance I have a table called Project where we allow a soft delete. What I mean by soft delete is in this table we have a bit field named Deleted if it is true that means the record was deleted, if false then it means it is a valid record.

Now this table has many relationships (tables that reference this project). For instance, a project can have multiple programs associated with it. So I have a ProgramProject table where a program can have multiple projects (1 to many relationship where 1 is the program and many is the projects). So when I delete a project (From what I mentioned above) the trigger should delete all Projects associated with it...

However, in some cases when a trigger executes it seems to return the error:

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.

Am I doing these triggers incorrectly? Here is an example of the trigger:

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

Am I doing this incorrectly due to it being a trigger? Should I be joining to some temp table instead of a single update statement?

Update

I tried to use M Ali's example but I was met with the same issue, subquery returned more than 1 row...

Here was the code I used:

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
Was it helpful?

Solution

Simply get the logically deleted rows into a temp table and than join that temp table with each related table and update the deleted fields in them tables.

You can simply use the deleted table itself to join with each table you want to update but inserting rows into temp table only where field deleted = 1 limits the number or rows in your temp table. and joining a relatively smaller table will give you some performance boost. please see below:

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

Update

Since you have mentioned you would like to update the table using a single projectID at a time. to achieve this you will need to create a while loop along with a temp table again and loop through your temp table to update records in each table projectid by projectid which I really think is an overkill for a fairly simply task. see below :

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top