【问题标题】:SQL Server 2016 T-SQL convert comma string to inner join database name(s)SQL Server 2016 T-SQL 将逗号字符串转换为内部连接数据库名称
【发布时间】:2017-11-16 21:14:38
【问题描述】:

我的某一列的值如下所示:

ID   | userPerms | Name      | DOB
-----+-----------+-----------+----------
5985 |1,3,4      |Bob Barker |12/12/1923
895  |1,2        |Bill Gates |10/14/1955
5897 |1,2,4      |Steve Jobs |02/24/1955

该列是 userPerms 列。

我需要能够Inner Join与与这些数字关联的userPerm 表

我的查询目前是:

SELECT 
    uT.employeeID + '|' + uT.lastFirstMiddle + '|' + uT.ntName + '|' + uT.email + '|' + 
    uT.firstName  + '|' + uT.lastName        + '|' + uT.active + '|' + uT.userPerms + '|' + 
    uT.userPermPages 
FROM 
    usersTbl AS uT
INNER JOIN 
    usersPermissions AS uP ON uP.id = uT.userPerms 
WHERE 
    uT.id = 1

由于数据中包含逗号,因此它自然不会起作用。

所以我在输出中寻找什么:

ID   | userPerms        | Name      | DOB
-----+------------------+-----------+------------
5985 |Read,Upload,Admin |Bob Barker |12/12/1923
895  |Read,Write        |Bill Gates |10/14/1955
5897 |Read,Write,Admin  |Steve Jobs |02/24/1955

有谁知道如何拆分这些,以便 inner join 能够按设计工作?

更新 1

我得到了它的工作,但是:

它不会将 userPerms 合并到原始字符串中

SELECT 
    uT.employeeID + '|' + uT.lastFirstMiddle + '|' + uT.ntName + '|' + 
    uT.email + '|' + uT.firstName + '|' + uT.lastName + '|' + uT.active, 
    (
        SELECT 
            ',' + uP.type
        FROM 
            usersPermissions AS uP
        WHERE 
            ',' + uT.userPerms + ',' 
        LIKE 
            '%,' + cast(uP.id AS nvarchar(20)) + ',%'
        FOR 
            XML PATH(''), TYPE
    ).value('substring(text()[1], 2)', 'varchar(max)') AS userPerms,
    (
        SELECT 
            ',' + uP.name
        FROM 
            pagePermissions AS uP
        WHERE 
            ',' + uT.userPerms + ',' 
        LIKE 
            '%,' + cast(uP.id AS nvarchar(20)) + ',%'
        FOR 
            XML PATH(''), TYPE
    ).value('substring(text()[1], 2)', 'varchar(max)') AS userPermPages
FROM 
    usersTbl as uT 
WHERE 
    uT.id = '1';

【问题讨论】:

  • 使用split函数和cross-apply怎么样?然后将结果转换为 CSV?
  • 你能修复数据结构吗?存储分隔数据违反了 1NF,并导致令人难以置信的痛苦。
  • 而且这种事情真的应该由角色而不是每个用户来处理。您将权限分配给角色,然后将用户分配给角色。让事物保持整洁。
  • SQL Server 2016 提供STRING_SPLIT()STRING_AGG() 使用第一个和CROSS APPLY 将您的ID 作为派生表,然后使用JOIN 绑定您的值,最后使用第二个重新连接结果。
  • @danihp 太真实了! Codd 保存 quee...ry .-D

标签: sql-server tsql sql-server-2016


【解决方案1】:

对于这样的 ID 列表来说,数据库设计不是很好。最好使用关系表来存储多对多关系。也就是说,这可能工作:

SELECT 
    uT.employeeID + '|' + uT.lastFirstMiddle + '|' + uT.ntName + '|' + uT.email + '|' + 
    uT.firstName  + '|' + uT.lastName        + '|' + uT.active + '|' + uT.userPerms + '|' + 
    uT.userPermPages 
FROM 
    usersTbl AS uT
INNER JOIN 
    usersPermissions AS uP
    ON ','+uT.userPerms+',' like '%,'+uP.id+',%'
WHERE 
    uT.id = 1

想法是在 uT.userPerms 的开头和结尾添加逗号,并使用 like 运算符加入 uT.userPerms 中出现的任何 uP.id(也用逗号包围)。

【讨论】:

  • 将 varchar 值 '%,' 转换为数据类型 int 时转换失败。
  • 使用 ON ','+uT.userPerms+',' like '%,'+ cast(uP.id as nvarchar(20)) +',%' 有效没有错误,但仍然显示数字。
【解决方案2】:

好吧,你问我... :-)

我目前没有安装 SQL-Server 2016,所以这是未经测试的空气代码。但我认为你需要这样的东西:

SET DATEFORMAT mdy;

DECLARE @tblData TABLE(ID INT, userPerms VARCHAR(100),Name VARCHAR(100),DOB DATE);
INSERT INTO @tblData VALUES
 (5985,'1,3,4','Bob Barker','12/12/1923')
,(895,'1,2','Bill Gates','10/14/1955')
,(5897,'1,2,4','Steve Jobs','02/24/1955');

DECLARE @tblPerm TABLE(ID INT, Perm VARCHAR(100));
INSERT INTO @tblPerm VALUES
 (1,'Read')
,(2,'Write')
,(3,'Upload')
,(4,'Admin');

SELECT d.ID,d.Name,d.DOB 
      ,STRING_AGG(p.Perm,',') AS Perms
FROM @tblData AS d
CROSS APPLY STRING_SPLIT(d.userPerms,',') AS A
INNER JOIN @tblPerm AS p ON CAST(A.value AS INT)=p.ID --<-- added cast to int
GROUP BY d.ID,d.Name,d.DOB;

函数string_split() 将用逗号分隔值,这允许您JOIN 您的目录表。 GROUP BY 结合 STRING_AGG() 应该返回你想要的。但是我以前从来没有用过这个......

希望这会有所帮助!

更新:没有string-splitstring-agg 的有效解决方案

SET DATEFORMAT mdy;

DECLARE @tblData TABLE(ID INT, userPerms VARCHAR(100),Name VARCHAR(100),DOB DATE);
INSERT INTO @tblData VALUES
 (5985,'1,3,4','Bob Barker','12/12/1923')
,(895,'1,2','Bill Gates','10/14/1955')
,(5897,'1,2,4','Steve Jobs','02/24/1955');

DECLARE @tblPerm TABLE(ID INT, Perm VARCHAR(100));
INSERT INTO @tblPerm VALUES
 (1,'Read')
,(2,'Write')
,(3,'Upload')
,(4,'Admin');

WITH Joined As
(
    SELECT d.ID
          ,d.Name
          ,d.DOB
          ,p.Perm 
    FROM @tblData AS d
    CROSS APPLY(SELECT CAST('<x>' +  REPLACE(d.userPerms,',','</x><x>') + '</x>' AS XML)) AS A(casted)
    CROSS APPLY A.casted.nodes(N'/x') AS B(numbers)
    INNER JOIN @tblPerm AS p ON CAST(B.numbers.value(N'text()[1]',N'int') AS INT)=p.ID
)
SELECT j.ID
      ,j.Name
      ,j.DOB 
      ,STUFF((
        SELECT ',' + x.perm
        FROM Joined AS x
        WHERE x.ID=j.ID
        FOR XML PATH('')
       ),1,1,'') AS Perm
FROM Joined AS j
GROUP BY ID,Name,DOB

【讨论】:

  • 'STRING_AGG' 不是可识别的内置函数名称。
  • @StealthRT 你确定,你在 SQL-Server 2016 上运行,目标数据库处于适当的兼容性级别,SSMS 是合适的版本吗?无法测试...阅读 MSDN 中的两个功能。如果您无法使其正常工作,有几种方法可以替换这些功能。
【解决方案3】:
create table  #tblData (ID INT, userPerms VARCHAR(100),Name VARCHAR(100),DOB DATE);
INSERT INTO #tbldata VALUES
 (5985,'1,3,4','Bob Barker','12/12/1923')
,(895,'1,2','Bill Gates','10/14/1955')
,(5897,'1,2,4','Steve Jobs','02/24/1955');

create table   #tblPerm (ID INT, Perm VARCHAR(100));
INSERT INTO #tblPerm VALUES
 (1,'Read')
,(2,'Write')
,(3,'Upload')
,(4,'Admin');    

Create FUNCTION [dbo].[fnSplitString] 
    ( 
        @string NVARCHAR(MAX), 
        @delimiter CHAR(1) 
    ) 
    RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
    ) 
    BEGIN 
        DECLARE @start INT, @end INT 
        SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
        WHILE @start < LEN(@string) + 1 BEGIN 
            IF @end = 0  
                SET @end = LEN(@string) + 1

            INSERT INTO @output (splitdata)  
            VALUES(SUBSTRING(@string, @start, @end - @start)) 
            SET @start = @end + 1 
            SET @end = CHARINDEX(@delimiter, @string, @start)

        END 
        RETURN 
    END
    go
    ;with cte
    as
    (
    select t1.ID,t1.userPerms,t1.Name,t3.Perm ,t1.DOB from #tblData as t1
    cross apply 
    dbo.fnsplitstring(t1.userPerms,',') as t2
    join #tblPerm as t3
    on t3.ID=t2.splitdata
    )

    select id,STUFF((select ','+perm from cte t1 
    where t1.ID=t2.id 
    for XML path('')),1,1,'')  as userperms,Name,DOB
    from cte t2
    group by id ,name,DOB

【讨论】:

    猜你喜欢
    • 2013-09-15
    • 1970-01-01
    • 2016-03-09
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多