5

I need to perform the following pseudo logic in a SQL Server 2012 procedure, based around a table variable and a table declared as such:

DECLARE @tmp TABLE
    (
    ID int IDENTITY(1,1),
    UserID int NOT NULL,
    SgsID int NOT NULL
    )

CREATE TABLE #Table1
    (
    ID int IDENTITY(1,1),
    UserID int NOT NULL,
    SgsID int NOT NULL
    )
  1. For each row of data in table variable @tmp
  2. Delete rows from Table1 where UserID/SgsID combinations match UserID/SgsID in Table1
  3. Delete those UserID/SgsID combinations from @tmp that have been deleted from Table1

I've been researching different approaches, such as using OUTPUT INTO and INTERSECT, but cannot write a query that deletes across two tables (in fact I don't think it is even possible).

I have achieved the above steps by using the following code, however, I was wondering if any T-SQL pro's may be able to suggest a more succinct/efficient approach?

See SQLFiddle for online version

CREATE TABLE #Table1
    (
    ID int IDENTITY(1,1),
    UserID int NOT NULL,
    SgsID int NOT NULL
    )

INSERT INTO #Table1 (UserID, SgsID) VALUES (5, 99)
INSERT INTO #Table1 (UserID, SgsID) VALUES (10, 89)
INSERT INTO #Table1 (UserID, SgsID) VALUES (150, 79)
INSERT INTO #Table1 (UserID, SgsID) VALUES (200, 69)
INSERT INTO #Table1 (UserID, SgsID) VALUES (250, 59)
SELECT * FROM #Table1 

DECLARE @tmp TABLE
    (
    ID int IDENTITY(1,1),
    UserID int NOT NULL,
    SgsID int NOT NULL
    )

INSERT INTO @tmp (UserID, SgsID) VALUES (150, 79)
INSERT INTO @tmp (UserID, SgsID) VALUES (200, 69)
INSERT INTO @tmp (UserID, SgsID) VALUES (250, 59)
INSERT INTO @tmp (UserID, SgsID) VALUES (999, 49)
SELECT * FROM @tmp

DECLARE @tbl_commonRows TABLE (UserID int, SgsID int)
INSERT INTO @tbl_commonRows 
    (
    UserID,
    SgsID
    ) 
SELECT 
    UserID,
    SgsID 
FROM
    #Table1
INTERSECT 
SELECT
    UserID,
    SgsID
FROM
    @tmp 

DELETE FROM 
    #Table1 
WHERE 
    (ID IN (
        SELECT 
            ID 
        FROM 
            #Table1 t1 INNER JOIN
            @tbl_commonRows c ON c.UserID = t1.UserID AND c.SgsID = t1.SgsID))

DELETE FROM
    @tmp
WHERE
    (ID IN (
        SELECT
            ID
        FROM
            @tmp t2 INNER JOIN
            @tbl_commonrows c ON c.UserID = t2.UserID AND c.SgsID = t2.SgsID))

SELECT * FROM #Table1 
SELECT * FROM @tmp
DROP TABLE #Table1
EvilDr
  • 8,943
  • 14
  • 73
  • 133

3 Answers3

4

Here's solution:

DECLARE @tmp_ids TABLE (
    id1 INT,
    id2 INT
)

INSERT INTO @tmp_ids (id1, id2)
SELECT 
    t1.id,
    t2.id
FROM Table1 t1
INNER JOIN tmp t2
    on (t1.UserID = t2.UserID AND t1.SgsID = t2.SgsID)

DELETE FROM Table1
WHERE id IN (SELECT id1 FROM @tmp_ids)

DELETE FROM tmp
WHERE id IN (SELECT id2 FROM @tmp_ids)

Keep in mind - i created physical tables tmp and Table1

Sergio
  • 6,900
  • 5
  • 31
  • 55
  • It won't work correctly. For example, if there are records `(250, 100)` and `(250, 59)` in `@tmp` table, and there is `(250, 59)` in `#Table1`. Both records will be deleted from `@tmp` although only `(250, 29)` is matched in `#Table1` and should be deleted from `@tmp`. – Ivan Golović Mar 06 '13 at 12:51
  • @Sergio If you actually tested it with a scenario that I proposed you would see it doesn't work for that particular case... – Ivan Golović Mar 06 '13 at 12:57
  • @IvanG Haha, just realized it works incorrect in sqlfiddle. But ssms does everything fine. Guess it's a sqlfiddle bug – Sergio Mar 06 '13 at 13:04
  • 1
    @Sergio It can't be SQL Fiddle bug since the site has SQL Server underneath it and just passes data through the interface. Anyway, I tested your solution in SSMS, not on SQL Fiddle, the solution does not work for cases like the one that I described and hopefully OP will test it for such case. – Ivan Golović Mar 06 '13 at 13:08
  • Sorry guys I'm getting confused. I created Sergio's idea in SQLFiddle and it seems to delete the correct rows from @tmp. Ivan G in your experience can you spot the problem, or clarify it further please? http://www.sqlfiddle.com/#!6/d41d8/3035 – EvilDr Mar 06 '13 at 13:31
  • 1
    @Sergio's query is OK, I misinterpreted parts of his query, it is OK, sorry for that... – Ivan Golović Mar 06 '13 at 13:38
2

You can take advantage of the fact that the OUTPUT command can take more than INSERTED and DELETED columns for deletes (but not inserts, sadly):

    DECLARE @output TABLE (id int) 

    DELETE FROM tbl
    OUTPUT tmp.ID INTO @output(id)
    FROM #Table1 tbl
    JOIN @tmp tmp 
        ON tbl.UserID = tmp.UserID 
        AND tbl.SgsID = tmp.SgsID

    DELETE FROM tmp
    FROM @tmp tmp
    JOIN @Output outp ON tmp.id = outp.id   
Jason Whitish
  • 1,428
  • 1
  • 23
  • 27
  • This is what I was hoping no one had posted yet when I encountered your answer. :) Nice one. – Andriy M Mar 06 '13 at 20:19
  • Wasn't aware of that one, thanks. The ID columns however may not be the same across both tables, as in UserID/sgsID may not have the same corresponding ID across tables. Is your code still relevant in that case please? – EvilDr Mar 07 '13 at 09:00
  • 1
    @EvilDr Yes; that's actually a reason to want to output the tmp.ID, so that you know that you're going back to the appropriate row in tmp when you do its delete. – Jason Whitish Mar 07 '13 at 14:55
1

Have you looked into using MERGE for this? Might be another option, and the syntax is nice and easy to follow.

MERGE (Transact-SQL)

steoleary
  • 8,968
  • 2
  • 33
  • 47
  • Could you possibly post an example to illustrate how to use it? I thought that because MERGE operates on a target table based on a source table it wouldn't be suitable here, as we are operating on both the source and target table...? – EvilDr Mar 07 '13 at 09:09
  • You can extend merge a bit by using the output clause to modify the source table: http://stackoverflow.com/questions/7331725/how-to-delete-from-source-using-merge-command-in-sql-server-2008 – steoleary Mar 07 '13 at 10:35
  • Played around with MERGE this morning - I absolutely love it. What an amazing addition. MERGE with OUTPUT is just excellent :-) – EvilDr Mar 08 '13 at 11:48