T-SQL合并两个逗号分隔列

时间:2016-06-08 18:27:14

标签: sql-server tsql merge

我试图将一个表合并到另一个表(我们称之为Stage和Prod)来控制用户及其权限。我的最终结果应该是一个Prod表,它将每个用户ID的权限从Stage合并到Prod。我遇到的问题是,这些表是由外部供应商设计的,并在一个以逗号分隔的列中包含多条信息。

舞台可能如下所示:

Userid     | Permissions
----------------------------------------------------------------
1          | schedule,upload,test,download,admin
2          | test,upload
3          | download

PROD:

Userid     | Permissions
----------------------------------------------------------------
1          | test,admin,schedule,download,upload
2          | admin
3          | download,upload

当他们合并时,用户ID应该拥有Stage的权限,并与Prod中的权限相结合。但是,当权限是逗号分隔的字符串时解决这个问题让我有机会结束。

在下面的最终结果中,用户ID 1的权限保持不变,因为它们在Stage中与Prod中的相同,只是顺序不同。 Userid 2已将他的Stage权限添加到他的Prod,因为他还没有这些权限。 用户ID 3的Prod权限不变,因为他的Stage权限已经包含在内。

结果:

Userid     | Permissions
----------------------------------------------------------------
1          | test,admin,schedule,download,upload
2          | admin,test,upload
3          | download,upload

有没有办法做到这一点?希望这有一定道理,但如果有更多的信息可以帮助我,我很乐意尝试提供它。谢谢你的任何帮助。

4 个答案:

答案 0 :(得分:0)

有趣的是,这是MSSQLTips blog Aaron Bertrand讨论的主题。借用他的代码,您可以创建Numbers表和字符串拆分/重组功能,以便进行以下工作。如果你打算经常这样做并且坚持你所展示的架构,那么这就是你要走的路。

/*Create Test Data

create table StagePermissions (UserID int, [Permissions] nvarchar(max));
create table ProdPermissions (UserID int, [Permissions] nvarchar(max));

insert StagePermissions values
(1,'schedule,upload,test,download,admin'),
(2,'test,upload'),
(3,'download')

insert ProdPermissions values
(1,'test,admin,schedule,download,upload'),
(2,'admin'),
(3,'download,upload')
*/

select sp.UserID, dbo.ReassembleString(sp.Permissions+','+pp.Permissions,',',N'OriginalOrder') MergedPermissions
from StagePermissions sp
    join ProdPermissions pp on pp.UserID=sp.UserID

答案 1 :(得分:0)

采用史蒂夫的测试数据,但添加:

create table BothPermissions (UserID int, [Permissions] nvarchar(max));

此代码将使用固定数量的可能权限。

DECLARE @XPermissions TABLE (
     UserID int
    ,XSchedule  BIT
    ,XUpload    BIT
    ,XTest      BIT
    ,XDownload  BIT
    ,XAdmin     BIT
    )

INSERT INTO @XPermissions
    SELECT
        ISNULL(sp.UserID,pp.UserID),
        CHARINDEX('schedule',sp.[Permissions]) + CHARINDEX('schedule',pp.[Permissions]),
        CHARINDEX('upload',sp.[Permissions]) + CHARINDEX('upload',pp.[Permissions]),
        CHARINDEX('test',sp.[Permissions]) + CHARINDEX('test',pp.[Permissions]),
        CHARINDEX('download',sp.[Permissions]) + CHARINDEX('download',pp.[Permissions]),
        CHARINDEX('admin',sp.[Permissions]) + CHARINDEX('admin',pp.[Permissions])

    FROM StagePermissions sp
        FULL JOIN ProdPermissions pp
            ON sp.UserID = pp.UserID

INSERT INTO BothPermissions
    SELECT
        UserID,
        CASE XSchedule WHEN 0 THEN '' ELSE 'schedule  ' END +
        CASE XUpload WHEN 0 THEN '' ELSE 'upload  ' END +
        CASE XTest WHEN 0 THEN '' ELSE 'test  ' END +
        CASE XDownload WHEN 0 THEN '' ELSE 'download  ' END +
        CASE XAdmin WHEN 0 THEN '' ELSE 'admin' END
    FROM @XPermissions

UPDATE BothPermissions
    SET [Permissions] = REPLACE(RTRIM([Permissions]),'  ',', ')

现在,我对史蒂夫的回答更加好奇。我认为这是最强大的解决方案。但是,我想知道它如何使用大型数据集。我仍然不知道答案,因为我还没有设置使用它所需的工具。但这是一个包含一些随机数生成的查询,用于填充每个记录的10,000条记录:

SELECT GETDATE()
DECLARE @StagePerms TABLE (
     UserID INT IDENTITY
    ,Perms  NVARCHAR(MAX)
    )

DECLARE @ProdPerms TABLE (
     UserID INT IDENTITY
    ,Perms  NVARCHAR(MAX)
    )

DECLARE @Counter INT = 0
DECLARE @XString NVARCHAR(MAX)

WHILE @Counter < 10000
    BEGIN
        SET @Counter += 1
            SET @XString = REPLACE(RTRIM(
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'test  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'admin  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'schedule  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'download  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'upload  ' END)
                ,'  ',', ') 
            INSERT INTO @StagePerms SELECT @XString
            SET @XString = REPLACE(RTRIM(
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'test  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'admin  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'schedule  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'download  ' END +
                CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'upload  ' END)
                ,'  ',', ')
            INSERT INTO @ProdPerms SELECT @XString
    END

SELECT GETDATE()

DECLARE @BothPerms TABLE (
     UserID INT
    ,Perms  NVARCHAR(MAX)
    )

DECLARE @XPerms TABLE (
     UserID int
    ,XSchedule  BIT
    ,XUpload    BIT
    ,XTest      BIT
    ,XDownload  BIT
    ,XAdmin     BIT
    )

INSERT INTO @XPerms
    SELECT
        ISNULL(sp.UserID,pp.UserID),
        CHARINDEX('schedule',sp.Perms) + CHARINDEX('schedule',pp.Perms),
        CHARINDEX('upload',sp.Perms) + CHARINDEX('upload',pp.Perms),
        CHARINDEX('test',sp.Perms) + CHARINDEX('test',pp.Perms),
        CHARINDEX('download',sp.Perms) + CHARINDEX('download',pp.Perms),
        CHARINDEX('admin',sp.Perms) + CHARINDEX('admin',pp.Perms)

    FROM @StagePerms sp
        FULL JOIN @ProdPerms pp
            ON sp.UserID = pp.UserID

INSERT INTO @BothPerms
    SELECT
        UserID,
        CASE XTest WHEN 0 THEN '' ELSE 'test  ' END +
        CASE XAdmin WHEN 0 THEN '' ELSE 'admin  ' END +
        CASE XSchedule WHEN 0 THEN '' ELSE 'schedule  ' END +
        CASE XDownload WHEN 0 THEN '' ELSE 'download  ' END +
        CASE XUpload WHEN 0 THEN '' ELSE 'upload  ' END

    FROM @XPerms

UPDATE @BothPerms
    SET Perms = REPLACE(RTRIM(Perms),'  ',', ')

SELECT * FROM @BothPerms

SELECT GETDATE()

随机数生成不到一秒钟;其余的大约需要31秒。史蒂夫,我有兴趣看一下比较。显然,如果数据不允许我的解决方案,则无关紧要。而且我确定某处有一个甜蜜的地方。

答案 2 :(得分:0)

请使用以下查询。它在SQL Server 2012中运行良好。

DECLARE @Stage TABLE (Userid int, Permission Varchar (8000))
DECLARE @Prod TABLE (Userid int, Permission Varchar (8000))
DECLARE @temp TABLE (Userid int, Permission Varchar (8000))

INSERT @Stage
(Userid,Permission)
VALUES
(1,'schedule,upload,test,download,admin'),
(2,'test,upload'),
(3,'download')

INSERT @Prod
(Userid,Permission)
VALUES
(1,'test,admin,schedule,download,upload'),
(2,'admin'),
(3,'download,upload')
-- Execution Part
INSERT INTO @temp
(Userid,Permission)
(
    SELECT A.Userid AS Userid,Split.a.value('.', 'VARCHAR(100)') AS Permission FROM  
    (SELECT Userid,CAST ('<M>' + REPLACE(Permission, ',', '</M><M>') + '</M>' AS XML) AS Permission FROM @Stage A) AS A 
    CROSS APPLY Permission.nodes ('/M') AS Split(a)
    UNION
    SELECT A.Userid AS Userid,Split.a.value('.', 'VARCHAR(100)') AS Permission FROM  
    (SELECT Userid,CAST ('<M>' + REPLACE(Permission, ',', '</M><M>') + '</M>' AS XML) AS Permission FROM @Prod A) AS A 
    CROSS APPLY Permission.nodes ('/M') AS Split(a)
)

SELECT Userid, Permission = 
        STUFF((SELECT ', ' + Permission
               FROM @temp b 
               WHERE b.Userid = a.Userid 
              FOR XML PATH('')), 1, 2, '')
FROM @temp a
GROUP BY Userid

输出

Userid  Permission
  1     admin, download, schedule, test, upload
  2     admin, test, upload
  3     download, upload

答案 3 :(得分:0)

您还可以使用SQL Serv 2016中引入的string splitting的直接支持(如果您当然已经开始使用此引擎版本:)) STRING_SPLIT返回单列表...