views:

194

answers:

5

I've created a sproc to delete multiple records by accepting a comma-delimited list of ID's as varchar, and using IN to attempt the deletion - doesn't work:

ALTER PROCEDURE [dbo].[sp_DeleteItemsFromItemCategories]
 @UserID bigint,
 @ItemsList varchar(8000)
AS
BEGIN
 -- SET NOCOUNT ON added to prevent extra result sets from
 -- interfering with SELECT statements.
 SET NOCOUNT ON;
 DELETE 
 FROM tbl_ItemFoldersLnk
 WHERE LineItemID IN (SELECT LineItemID FROM tbl_ItemFoldersLnk WHERE LineItemID IN (@ItemsList))
END

Initially I had ... IN (@ItemList) but still does not work. The error is "Error converting data type varchar to bigint."

I do have other SPROCS that iterate through comma delimited lists which I could use to delete, but then I'm running a delete function for each row.

Suggestions?

Thanks.

+1  A: 

Arrays and lists in SQL Server.

You need to change the CSV into a table (as per article) and join to filter.

You are literally comparing the ID columns with the string "1, 45, 67" which is not an integer...

gbn
A: 

You could parse the input string into discrete values (by e.g. a user-defined-function), inserting these values into a table object, and then issuing a delete command that uses the IN operation against all values in your table variable....

...or you could send XML to your procedure and use the FOR XML operation to do similar (i.e. parse the XML into a table variable etc.etc.

Either way, you'll need to end up with table-like data rather than a comma-delimited string.

davek
A: 

Looks like SQL Server code. If so, Here's a UDF to convert a delimited list into a table:

Create Function [dbo].[ParseTextString] (@S Text, @delim VarChar(5))
Returns @tOut Table 
    (ValNum Integer Identity Primary Key, 
     sVal VarChar(8000))
As
Begin 
Declare @dLLen TinyInt        -- Length of delimiter
Declare @sWin  VarChar(8000)  -- Will Contain Window into text string
Declare @wLen  Integer        -- Length of Window
Declare @wLast TinyInt        -- Boolean to indicate processing Last Window
Declare @wPos  Integer        -- Start Position of Window within Text String
Declare @sVal  VarChar(8000)  -- String Data to insert into output Table
Declare @BtchSiz Integer      -- Maximum Size of Window
     Set @BtchSiz = 7900      -- (Reset to smaller values to test routine)
Declare @dPos Integer         -- Position within Window of next Delimiter
Declare @Strt Integer         -- Start Position of each data value within Window
-- -------------------------------------------------------------------------

    -- Default delimiter is pipe char -----
    If @delim is Null Set @delim = '|' 
    If DataLength(@S) = 0 Or
        Substring(@S, 1, @BtchSiz) = @delim Return
    -- ---------------------------
    Select @dLLen = Len(@delim),
        @Strt = 1, @wPos = 1,
        @sWin = Substring(@S, 1, @BtchSiz)
    Select @wLen = Len(@sWin),
        @wLast = Case When Len(@sWin) = @BtchSiz
         Then 0 Else 1 End,
         @dPos = CharIndex(@delim, @sWin, @Strt)
    -- ----------------------------
    While @Strt <= @wLen
    Begin
         If @dPos = 0 -- No More delimiters in window
         Begin    
             If @wLast = 1 Set @dPos = @wLen + 1 
             Begin
                  Set @wPos = @wPos + @Strt - 1
                  Set @sWin = Substring(@S, @wPos, @BtchSiz)
                  -- ----------------------------------------
                  Select @wLen = Len(@sWin), @Strt = 1,
                        @wLast = Case When Len(@sWin) = @BtchSiz
                                  Then 0 Else 1 End,
                         @dPos = CharIndex(@delim, @sWin, 1)
                  If @dPos = 0 Set @dPos = @wLen + 1 
             End
        End
        -- -------------------------------
        Set @sVal = LTrim(Substring(@sWin, @Strt, @dPos - @Strt))
        Insert @tOut (sVal) Values (@sVal)
        -- -------------------------------
        -- Move @Strt to char after last delimiter
         Set @Strt = @dPos + @dLLen 
         Set @dPos = CharIndex(@delim, @sWin, @Strt)
     End
   Return 
 End
Charles Bretana
dearie me. Have you seen simpler code and techniques here: http://www.sommarskog.se/arrays-in-sql-2005.html
gbn
@gbn, This handles input strings of any size ( > 8k bytes), and arbitrary multi-Char delimiters... I needed thios for a gig some time ago, and have kept this as my standard delimited string parser since...
Charles Bretana
+1  A: 

Assuming the string is a comma delimited list of integer values, try this:

SET @ItemsList= ','+replace(@ItemsList,' ','')+','

Now the list should have every number delimited by a comma and extra spaces removed. The query for the delete becomes

DELETE FROM tbl_ItemFoldersLnk WHERE Charindex(','+ltrim(str(LineItemID))+',',@ItemList) > 0

Performance will not be great, but it will get the job done...

Sparky
A: 

I typically convert the CSV into a table. However, another simple way to do this is to build a string and exec or sp_executesql the string. This has some security implications. You may need to manage permissions differently because the caller will need permission to exec the stored proc as well as permission to delete from the table.

DECLARE @sql varchar(max)

SELECT @sql = 'DELETE FROM tbl_ItemFoldersLnk WHERE LineItemID IN (' + @sql + ')'

exec(@sql)