insert/copy deleted rows from within while loop

  • Trying to demonstrate that existing code is deleting more than it should.

    But I can't figure out how to complete this test.

    DECLARE @Results TABLE

    (

    Result FLOAT,

    Result_on DATETIME,

    Target FLOAT

    )

    DECLARE @ResultsShadow TABLE -- my addition for testing

    (

    result FLOAT,

    result_on DATETIME,

    Target FLOAT

    )

    DECLARE @Alarms TABLE

    (

    Id INT IDENTITY,

    StartTime DATETIME,

    EndTime DATETIME,

    Action1 INT

    )

    DECLARE @DebugTable TABLE -- my addition for testing

    (

    RowsAffected INT, -- ignore

    SanityCheck INT, -- ignore

    AlarmStart DATETIME,

    AlarmEnd DATETIME,

    Result_On DATETIME,

    AlarmId INT,

    [Target] REAL,

    Result REAL

    )

    SET @i = 1

    WHILE @i IS NOT NULL

    BEGIN

    SELECT @AlarmStart = StartTime, @AlarmEnd = EndTime

    FROM @Alarms

    WHERE Id = @i

    IF (@IncludeAlarms = 0)

    BEGIN

    DELETE FROM @Results WHERE Result_On >= @AlarmStart -- original condition

    AND Result_On < @AlarmEnd -- original condition

    OUTPUT DELETED.* INTO @ResultsShadow-- from here down to...

    BEGIN

    INSERT INTO @DebugTable(

    Result_On,

    AlarmId,

    [Target],

    Result)

    SELECT

    @AlarmStart,

    @AlarmEnd,

    Result_On

    FROM @Results

    END

    END -- ... here is my feeble additions

    SELECT @i = min(Id) FROM @Alarms WHERE id > @i

    END

    The while loop is iterating through the @Alarms table, and selecting a value for @AlarmStart and @AlarmEnd.

    Then (based on a condition) each row of @Results table is compared with these values and any matches found are deleted.

    I know more than one row is deleted from @Results during each iteration because I used @@ROWCOUNT to prove it.

    The problem is there should be only a one-to-one match. At most only one row should be deleted per iteration/match. And I need to capture more information about those deletions to highlight the problem.

    But I'm having a hard time figuring out how to grab that info and putting it, along with the @AlarmStart, @AlarmEnd condition, into the @DebugTable, from within the loop.

    I have to keep this loop structure intact because this is existing code and easier to demonstrate the problem if I'm not re-writing it or making significant changes.

    So I'm just trying to capture the details of what it is deleting and the conditions for each deletion.

    I thought maybe if I OUTPUTTED the deleted rows into the @ResultsShadow table (that I made), then I could use

    it's contents and push them into the @DebugTable, along with the respective @AlarmStart, @AlarmEnd values.

    Then clear the @ResultsShadow table for the next iteration.

    But I couldn't get that to work and maybe there is a better way.

    Suggestions?

    UPDATE - thanks to John for pointing out that the WHERE goes after the OUTPUT.

    SET @i = 1

    WHILE @i IS NOT NULL

    BEGIN

    SELECT @AlarmStart = StartTime, @AlarmEnd = EndTime,

    FROM @Alarms

    WHERE Id = @i

    IF (@IncludeAlarms = 0)

    BEGIN

    DELETE FROM @Results

    OUTPUT DELETED.* INTO @ResultsShadow

    WHERE Result_On >= @AlarmStart

    AND Result_On < @AlarmEnd

    IF @@ROWCOUNT > 0

    BEGIN

    INSERT INTO @DebugTable(

    AlarmStart,

    AlarmEnd,

    Result_On,

    AlarmId,

    [Target],

    Result)

    SELECT

    @AlarmStart, -- from this iteration, just one val

    @AlarmEnd, -- from this iteration, just one val

    result_on, -- from @ResultsShadow

    @i, -- from this iteration, just one val

    [Target], -- from @ResultsShadow

    result -- from @ResultsShadow

    FROM @ResultsShadow

    DELETE FROM @ResultsShadow-- empty it for next iteration

    END

    END

    SELECT @i = min(Id) FROM @Alarms WHERE id > @i

    END

  • Quick question, have you tested if @i ever becomes NULL?

    😎

    WHILE @i IS NOT NULL

    ...

    SELECT @i = min(Id) FROM @Alarms WHERE id > @i

  • Eirikur Eiriksson (12/6/2016)


    Quick question, have you tested if @i ever becomes NULL?

    😎

    WHILE @i IS NOT NULL

    ...

    SELECT @i = min(Id) FROM @Alarms WHERE id > @i

    Uh... no. I assume the while loop would then exit if @i becomes NULL. I'm not following your train of thought.

  • Not sure whether this is an option for you, but my suggestion is just to ditch the loop. You can replace the whole lot with something like this:DELETE FROM @Results

    WHERE Result_On >= (SELECT MIN(StartTime) FROM @Alarms)

    AND Result_On < (SELECT MAX(EndTime) FROM @Alarms)

    You could even rewrite the above so it only needs to do a single scan of the @Alarms table, something like this:DELETE r

    FROM @Results r

    JOIN (

    SELECT

    MIN(StartTime) AS EarliestStart

    ,MAX(EndTime) AS LatestEnd

    ) m

    ON r.Result_On >= m.EarliestStart

    AND r.Result_On < m.LatestEnd

    With no sample data, I haven't tested either of these two queries, but even if they don't work first time, they'll be a place to start.

    John

  • John Mitchell-245523 (12/6/2016)


    Not sure whether this is an option for you, but my suggestion is just to ditch the loop. You can replace the whole lot with something like this:DELETE FROM @Results

    WHERE Result_On >= (SELECT MIN(StartTime) FROM @Alarms)

    AND Result_On < (SELECT MAX(EndTime) FROM @Alarms)

    You could even rewrite the above so it only needs to do a single scan of the @Alarms table, something like this:DELETE r

    FROM @Results r

    JOIN (

    SELECT

    MIN(StartTime) AS EarliestStart

    ,MAX(EndTime) AS LatestEnd

    ) m

    ON r.Result_On >= m.EarliestStart

    AND r.Result_On < m.LatestEnd

    With no sample data, I haven't tested either of these two queries, but even if they don't work first time, they'll be a place to start.

    John

    Unfortunately it is not an option. Yeah even my noobie-SQL senses asked if that loop is even necessary.

    So I'm not surprised that there are better alternatives.

    But I can't change the code without completely justifying it.

    Which brings me back to my main point of trying to demonstrate the faulty logic of this code - if there is any - by gathering more information regarding the DELETE section.

  • OK, fair enough.

    The OUTPUT clause goes before the WHERE clause. Are you not getting a syntax error?

    Some sample data would be very helpful here. If the StartTime to EndTime period on any of your rows overlaps with that of any other row, then you can't guarantee a one-to-one mapping.

    John

  • Just include the @id and id values in your debug table.

    INSERT INTO @DebugTable(

    input_id,

    id,

    Result_On,

    AlarmId,

    [Target],

    Result)

    SELECT

    @id,

    id,

    @AlarmStart,

    @AlarmEnd,

    Result_On

    FROM @Results

    Drew

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

Viewing 7 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic. Login to reply