[英]SQL Server stored procedure looping through a comma delimited cell
我试图弄清楚如何获取存在于我的一个单元格中的逗号分隔字符串的值。
这是我当前试图在存储过程中查询的查询:
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
表如下所示:
因此, usersTbl
表中的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'
首先,您应该阅读在数据库列中存储分隔列表真的那么糟糕吗? ,您会在很多地方看到这个问题的答案是肯定的!
其次,您应该添加一个用户权限表,因为这显然是多对多关系。 您的表可能看起来像这样(伪代码):
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)
)
正确设置表后,从权限表中获取逗号分隔值的列表非常容易。
使scsimon的示例数据脚本适应正确的多对多关系:
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
结果:
Name Id Permissions Age
Bbarker 5987 Read,Upload 87
我同意所有评论...但是严格尝试做您想做的事,这是一种使用拆分器功能的方法
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
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
我同意其他答复中向您提供的许多建议。 您开始使用的结构将很难维护和使用。 但是,您的情况可能意味着您会坚持下去,因此以下某些工具可能会为您提供帮助。
您可以使用charindex()来解析定界符,如此处演示的其他内容-MSSQL- 如何使用逗号作为分隔符来分隔字符串
...甚至更好(这里提供了几个功能) -T-SQL中的拆分功能是否等效?
如果您仍想使用原始内联SQL并将其提交到循环中,则将字符串操作与CURSOR配对。 游标有他们自己的争议BTW。 如果您的权限语法保持一致,则下面的代码将起作用,但事实可能并非如此。
他们使用charindex(',',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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.