SQL Server存储过程循环通过逗号分隔的单元格

时间:2017-08-15 15:48:34

标签: sql sql-server stored-procedures sql-server-2014

我正在试图弄清楚如何获取我的一个单元格中存在的逗号分隔字符串的值。

这是我当前试图在我的存储过程中弄清楚的查询:

SELECT 
   uT.id, 
   uT.permissions
FROM 
   usersTbl AS uT
INNER JOIN 
   usersPermissions AS uP 
   /*Need to loop here I think?*/
WHERE 
   uT.active = 'true'
AND 
   uT.email = 'bbarker@thepriceisright.com'

usersPermissions表如下所示:

enter image description here

所以usersTbl表格中的一行对于permissions来说就是这样:

1,3

我需要找到一种方法来遍历该单元格并获取每个数字并将名称****放在我usersTbl.permissions的返回结果中。

所以不要回复:

Name    | id   | permissions | age |
------------------------------------
Bbarker | 5987 | 1,3         | 87  |

需要返回:

Name    | id   | permissions | age |
------------------------------------
Bbarker | 5987 | Read,Upload | 87  |

真的只是将1,3替换为Read,Upload

来自SQL GURU的任何帮助都会很棒!

重新设计的查询

 SELECT 
     * 
 FROM
     usersTbl AS uT 
 INNER JOIN 
     usersPermissionsTbl AS uPT 
 ON 
     uPT.userId = uT.id 
 INNER JOIN 
     usersPermissions AS uP 
 ON 
     uPT.permissionId = uP.id 
 WHERE 
     uT.active='true'
 AND 
     uT.email='bBarker@thepriceisright.com'

3 个答案:

答案 0 :(得分:2)

首先,你应该阅读Is storing a delimited list in a database column really that bad?,你会看到很多理由为什么这个问题的答案是绝对是的!

其次,您应该为用户权限添加一个表,因为这显然是多对多的关系。 你的表可能看起来像这样(伪代码):

 firebase.initializeApp(config);

var messaging = firebase.messaging();
messaging.requestPermission()
    .then(function () {

        messaging.onMessage(function(payload) {
            console.log("Message received. ", payload);
            // ...
        });
        console.log('I am in here');

        return messaging.getToken()
            .then(function (currentToken) {
                console.log(currentToken);
            })
            .catch(function (err) {
                console.log('An error occurred while retrieving token. ', err);
                showToken('Error retrieving Instance ID token. ', err);
                setTokenSentToServer(false);
            });



    }).catch(function (err) {
        console.log('Error');
    });

messaging.onMessage(function(payload) {
    console.log("Message received. ", payload);
    // ...
});

一旦表格正确,就可以很容易地从权限表中获取逗号分隔值列表。

将scsimon的示例数据脚本调整为正确的多对多关系:

usersTbl
(
    Id int primary key
    -- other user related columns
)

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

permissionsTbl
(
    Id int primary key,
    Name varchar(20)
)

现在查询如下:

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

insert into @users values 
('Bbarker',5987,87)

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

insert into @permissions values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')

declare @usersPermissions as table (userId int, permissionId int)

insert into @usersPermissions values (5987, 1), (5987, 3)

结果:

SELECT  u.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, '') As Permissions,
        u.Age
FROM @Users As u

You can see a live demo on rextester.

答案 1 :(得分:2)

我同意所有的评论......但是严格地尝试做你想做的事情,这是一种带有分离器功能的方法

declare @usersTbl table ([Name] varchar(64), id int, [permissions] varchar(64), age int)
insert into @usersTbl
values

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

declare @usersTblpermissions table (id int, [type] varchar(64))
insert into @usersTblpermissions
values
(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
    inner join @usersTblpermissions p on p.id = x.Item)

select distinct
    [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

CREATE FUNCTION [dbo].[DelimitedSplit8K] (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!

RETURNS TABLE WITH SCHEMABINDING AS
RETURN

/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/

  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM 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 (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@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(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
GO

答案 2 :(得分:1)

我同意其他回复中提出的大部分建议。您开始使用的结构在维护和使用方面不会很有趣。但是,您的情况可能意味着您仍然坚持使用它,所以下面的一些工具可能会对您有所帮助。

您可以使用charindex()解析分隔符,正如其他人在此处演示的那样 - MSSQL - How to split a string using a comma as a separator

......在这里甚至更好(提供了几个功能) - Split function equivalent in T-SQL?

如果您仍希望使用原始内联SQL并将其提交到循环,则将字符串操作与CURSOR配对。游标有自己的争议BTW。如果您的权限语法保持一致,则下面的代码将起作用,它可能没有。

他们使用charindex(&#39;,&#39;,columnName)并将位置输入left()和right()函数以及一些额外的字符串评估以将值拉出。你应该可以用光标将它们拼凑起来

您的查询可能如下所示......

--creating my temp structure
declare @userPermissions table (id int, [type] varchar(16))
insert into @userPermissions (id, [type]) values (1, 'Read')
insert into @userPermissions (id, [type]) values (2, 'Write')
insert into @userPermissions (id, [type]) values (3, 'Upload')
insert into @userPermissions (id, [type]) values (4, 'Admin')

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

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