views:

4764

answers:

7

I'm not sure if this is something I should do in T-SQL or not, and I'm pretty sure using the word 'iterate' was wrong in this context, since you should never iterate anything in sql. It should be a set based operation, correct? Anyway, here's the scenario:

I have a stored proc that returns many uniqueidentifiers (single column results). These ids are the primary keys of records in a another table. I need to set a flag on all the corresponding records in that table.

How do I do this without the use of cursors? Should be an easy one for you sql gurus!

+16  A: 

This may not be the most efficient, but I would create a temp table to hold the results of the stored proc and then use that in a join against the target table. For example:

CREATE TABLE #t (uniqueid int)
INSERT INTO #t EXEC p_YourStoredProc

UPDATE TargetTable 
SET a.FlagColumn = 1
FROM TargetTable a JOIN #t b 
    ON a.uniqueid = b.uniqueid

DROP TABLE #t
Ben Hoffstein
Excellent! I'd only add a "DROP TABLE #t" after the UPDATE. If you're on SQL Server 2005, you can also use a table variable instead of a temp table with INSERT INTO - table variables are sandboxed and auto-destroy on process exit. They're also faster most of the times (live in memory).
Joe Pineda
URBAN MYTH: Table variables DO NOT live in memory! They live in tempDB. Plesae do not propogate this myth!
Mitch Wheat
@Mitch Wheat, indeed you're right. Never knew that!
Ben Hoffstein
@Joe Pineda, I added the drop table for completeness.
Ben Hoffstein
@Mitch: You had me thinking, so I investigated a bit, found this:support.microsoft.com/kb/305977Q4 is exactly this one - are table variables in-memory structures? No, they're created also on disk. Then, if memory's available, BOTH temp tables and table variables are processed in memory!!!Thanks!
Joe Pineda
A: 

An ugly solution would be to have your procedure return the "next" id each time it is called by using the other table (or some flag on the existing table) to filter out the rows that it has already returned

hamishmcn
A: 

Insert the results of the stored proc into a temporary table and join this to the table you want to update:

INSERT INTO #WorkTable
EXEC usp_WorkResults

UPDATE DataTable
  SET Flag = Whatever
FROM DataTable
INNER JOIN #WorkTable
  ON DataTable.Ket = #WorkTable.Key
Cade Roux
A: 

Use temporary tables or a table variable (you are using SS2005).

Although, that's not nest-able - if a stored proc uses that method then you can't dumpt that output into a temp table.

Unsliced
A: 

You can use a temp table or table variable with an additional column:

DECLARE @MyTable TABLE (
    Column1 uniqueidentifer,
    ...,
    Checked bit
)

INSERT INTO @MyTable
SELECT [...], 0 FROM MyTable WHERE [...]

DECLARE @Continue bit
SET @Continue = 1
WHILE (@Continue)
BEGIN
    SELECT @var1 = Column1,
           @var2 = Column2,
           ...
    FROM @MyTable
    WHERE Checked = 1

    IF @var1 IS NULL
        SET @Continue = 0
    ELSE
    BEGIN

        ...

        UPDATE @MyTable SET Checked = 1 WHERE Column1 = @var1
    END
END

Edit: Actually, in your situation a join will be better; the code above is a cursorless iteration, which is overkill for your situation.

technophile
A: 

If you upgrade to SQL 2008 then you can pass table parameters I believe. Otherwise, you're stuck with a global temporary table or creating a permanent table that includes a column for some sort of process ID to identify which call to the stored procedure is relevant.

How much room do you have in changing the stored procedure that generates the IDs? You could add code in there to handle it or have a parameter that lets you optionally flag the rows when it is called.

Tom H.
+2  A: 

You could also change your stored proc to a user-defined function that returns a table with your uniqueidentifiers. You can joing directly to the UDF and treat it like a table which avoids having to create the extra temp table explicitly. Also, you can pass parameters into the function as you're calling it, making this a very flexible solution.

CREATE FUNCTION dbo.udfGetUniqueIDs
()
RETURNS TABLE 
AS
RETURN 
(
    SELECT uniqueid FROM dbo.SomeWhere
)

GO

UPDATE dbo.TargetTable 
SET a.FlagColumn = 1
FROM dbo.TargetTable a INNER JOIN dbo.udfGetUniqueIDs() b 
    ON a.uniqueid = b.uniqueid

Edit: This will work on SQL Server 2000 and up...

Codewerks
Good idea, though remember not all stored procedures are convertible into UDFs for these need to be deterministic and only call other deterministic pieces of code. If Kilhoffer's SP is one of these, he'll have to go the temp table route.
Joe Pineda