Skip to content Skip to sidebar Skip to footer

Sql Server Stored Procedure Looping Through A Comma Delimited Cell

I am trying to figure out how to go about getting the values of a comma separated string that's present in one of my cells. This is the query I current am trying to figure out in m

Solution 1:

I agree with all of the comments... but strictly trying to do what you want, here's a way with a splitter function

declare@usersTbltable ([Name] varchar(64), id int, [permissions] varchar(64), age int)
insertinto@usersTblvalues

('Bbarker',5987,'1,3',87)

declare@usersTblpermissionstable (id int, [type] varchar(64))
insertinto@usersTblpermissionsvalues
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')

;with cte as(
    select
        u.[Name]
        ,u.id as UID
        ,p.id
        ,p.type
        ,u.age
    from@usersTbl u
    cross apply dbo.DelimitedSplit8K([permissions],',') x
    innerjoin@usersTblpermissions p on p.id = x.Item)

selectdistinct
    [Name]
    ,UID
    ,age
    ,STUFF((
          SELECT','+ t2.type
          FROM cte t2
          WHERE t.UID = t2.UID
          FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from cte t

Jeff Moden Splitter

CREATEFUNCTION [dbo].[DelimitedSplit8K] (@pStringVARCHAR(8000), @pDelimiterCHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!RETURNSTABLEWITH SCHEMABINDING ASRETURN/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/WITH E1(N) AS (
                 SELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1UNIONALLSELECT1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT1FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT1FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front-- for both a performance gain and prevention of accidental "overruns"SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDERBY (SELECTNULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)SELECT1UNIONALLSELECT t.N+1FROM cteTally t WHERESUBSTRING(@pString,t.N,1) =@pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.SELECT ItemNumber =ROW_NUMBER() OVER(ORDERBY l.N1),
        Item       =SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
GO

Solution 2:

First, you should read Is storing a delimited list in a database column really that bad?, where you will see a lot of reasons why the answer to this question is Absolutely yes!

Second, you should add a table for user permissions since this is clearly a many to many relationship. Your tables might look something like this (pseudo code):

usersTbl
(
    Id intprimary key
    -- other user related columns
)

usersPermissionsTbl
(
    UserId int, -- Foreign key to usersTbl
    PermissionId int, -- Foreign key to permissionsTblPrimary key (UserId, PermissionId)
)

permissionsTbl
(
    Id intprimary key,
    Name varchar(20)
)

Once you have your tables correct, it's quite easy to get a list of comma separated values from the permissions table.

Adapting scsimon's sample data script to a correct many to many relationship:

declare@userstable ([Name] varchar(64), id int, age int)

insertinto@usersvalues 
('Bbarker',5987,87)

declare@permissionstable (id int, [type] varchar(64))

insertinto@permissionsvalues
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')

declare@usersPermissionsastable (userId int, permissionId int)

insertinto@usersPermissionsvalues (5987, 1), (5987, 3)

Now the query looks like this:

SELECTu.Name,
        u.Id,
        STUFF(
        (
            SELECT ','+ [type]
            FROM @permissions p
            INNER JOIN @usersPermissions up ON p.id = up.permissionId
            WHERE up.userId = u.Id
            FOR XML PATH('')
        )
            , 1, 1, '') AsPermissions,
        u.AgeFROM @UsersAsu

And the results:

NameIdPermissionsAgeBbarker5987    Read,Upload87

You can see a live demo on rextester.

Solution 3:

I concur with much of the advice being presented to you in the other responses. The structure you're starting with is not going to be fun to maintain and work with. However, your situation may mean you are stuck with it so maybe some of the tools below will help you.

You can parse the delimiter with charindex() as others demonstrated here- MSSQL - How to split a string using a comma as a separator

... and even better here (several functions are provided) - Split function equivalent in T-SQL?

If you still want to do it with raw inline SQL and are committed to a loop, then pair the string manipulation with a CURSOR. Cursors have their own controversies BTW. The code below will work if your permission syntax remains consistent, which it probably doesn't.

They used charindex(',',columnName) and fed the location into the left() and right() functions along with some additional string evaluation to pull values out. You should be able to piece those together with a cursor

Your query might look like this...

--creating my temp structuredeclare@userPermissionstable (id int, [type] varchar(16))
insertinto@userPermissions (id, [type]) values (1, 'Read')
insertinto@userPermissions (id, [type]) values (2, 'Write')
insertinto@userPermissions (id, [type]) values (3, 'Upload')
insertinto@userPermissions (id, [type]) values (4, 'Admin')

declare@usersTbltable ([Name] varchar(16), id int, [permissions] varchar(8), age int)
insertinto@usersTbl ([Name], id, [permissions], age) values ('Bbarker', 5987, '1,3', 87)
insertinto@usersTbl ([Name], id, [permissions], age) values ('Mmouse', 5988, '2,4', 88)

--example queryselect 
    ut.[Name]
   ,  (select [type] from@userPermissionswhere [id] =left(ut.[permissions], charindex(',', ut.[permissions])-1) )
    +','+ (select [type] from@userPermissionswhere [id] =right(ut.[permissions], len(ut.[permissions])-charindex(',', ut.[permissions])) )
from@usersTbl ut

Post a Comment for "Sql Server Stored Procedure Looping Through A Comma Delimited Cell"