基于逗号分隔的字段比较更新列

时间:2016-03-22 11:20:19

标签: sql sql-server

我有一个包含ClientID,Name

列的客户端表
ClientID|Name
1       |A
2       |B
3       |C
4       |D
5       |E

包含列的Users表:UserID,AllowedClients,DeniedClients

USERID |AllowedClients|DeniedClients
U1     |1,2,5         |NULL
U2     |2,1           |NULL
U3     |1,4,5         |NULL
U4     |5,1,3         |NULL
U5     |2,3           |NULL

目前所有DeniedClients都设置为NULL。

我需要更新用户表, 类似的东西:

Update users set DeniedClients= (All existing ClientIDs in Client table) –` (users.AllowedClients)

输出:

USERID |AllowedClients|DeniedClients
U1     |1,2,5         |3,4
U2     |2,1           |3,4,5
U3     |1,4,5         |2,3
U4     |5,1,3         |2,4
U5     |2,3           |1,4,5

如何在不使用游标的情况下编写此查询。

2 个答案:

答案 0 :(得分:1)

正如其他人所说,结构很糟糕......但仍有办法实现,这就是我在T-SQL中的表现......

编辑:修复了1与1匹配的部分...需要更多测试,但正在寻找

if object_id('tempdb..#Client') is not null drop table #Client
create table #Client (ClientID int, Name nvarchar(5))

insert into #Client(ClientID, Name)
values
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D'),
(5, 'E'),
(11, 'F')

if object_id('tempdb..#Users') is not null drop table #Users
create table #Users (UserID nvarchar(5),AllowedClients nvarchar(50),
                     DeniedClients nvarchar(50) null)

insert into #Users (UserID, AllowedClients)
values
('U1', '1, 2, 5'),
('U2', '2, 1'),
('U3', '1, 4, 5'),
('U4', '5, 1, 3'),
('U5', '2, 3'),
('U6', '11, 4, 5')

    update usr1
set DeniedClients = (STUFF((SELECT ', ' + cast(clt.ClientID as nvarchar)
                    from #Users usr
                join #Client clt 
                on  (usr.AllowedClients  not like 
                    '%' + cast(clt.ClientID as nvarchar) + '%' and 
                    SUBSTRING(usr.AllowedClients, 
                    PATINDEX('%' + cast(clt.ClientID as nvarchar) + '%' , 
                    usr.AllowedClients) + 1, 1) <> cast(clt.ClientID as nvarchar))
                or
                    (usr.AllowedClients  like 
                    '%' + cast(clt.ClientID as nvarchar) + '%' and 
                    SUBSTRING(usr.AllowedClients, 
                    PATINDEX('%' + cast(clt.ClientID as nvarchar) + '%' ,
                    usr.AllowedClients) + 1, 1) = cast(clt.ClientID as nvarchar))
                    where usr.UserID = usr1.UserID
                    FOR XML PATH(''), TYPE
                    ).value('.', 'NVARCHAR(MAX)'),1,1,''))
from #Users usr1

select * from #Users

答案 1 :(得分:0)

以下输出由以下代码生成:

enter image description here

DECLARE @Client TABLE
(
    [ClientID] TINYINT
   ,[Name] VARCHAR(12)
)

INSERT INTO @Client ([ClientID], [Name])
VALUES (1, 'A')
      ,(2, 'B')
      ,(3, 'C')
      ,(4, 'D')
      ,(5, 'E');

DECLARE @Users TABLE
(
    [UserID] CHAR(2)
   ,[AllowedClients] VARCHAR(32)
   ,[DeniedClients] VARCHAR(32)
);

INSERT INTO @Users ([UserID], [AllowedClients], [DeniedClients])
VALUES ('U1', '1,2,5', NULL)
      ,('U2', '2,1', NULL)
      ,('U3', '1,4,5', NULL)
      ,('U4', '5,1,3', NULL)
      ,('U5', '2,3', NULL);

WITH DataSource ([UserID], [ClientID]) AS
(
    -- getting all combinations
    SELECT U.[UserID]
          ,C.[ClientID]
    FROM @Users U
    CROSS APPLY @Client C
    -- excluding current values
    EXCEPT
    -- getting current values
    SELECT [UserID]
          ,T.c.value('(./text())[1]', 'nvarchar(4000)') [ClientID]
    FROM @Users U
    CROSS APPLY 
    ( 
        SELECT x = CONVERT(XML, '<i>'  + REPLACE([AllowedClients], ',', '</i><i>') + '</i>').query('.')
    ) DS
    CROSS APPLY x.nodes('i') AS T(c)
) 
SELECT DISTINCT DS1.[UserID]
      ,DS.[CSV]
FROM DataSource DS1
CROSS APPLY
(
    SELECT STUFF
    (
        (
            SELECT CONCAT(',', DS2.[ClientID])
            FROM DataSource DS2
            WHERE DS1.[UserID] = DS2.[UserID]
            FOR XML PATH(''), TYPE
        ).value('.', 'varchar(max)')
    ,1
    ,1
    ,''
    )
) DS(CSV)

您现在可以执行更新。

如果这是一个真实的项目并且有很多记录,那么规范化数据会更快。如果由于其他原因而坚持使用CSV格式,则应编写一些CLR函数来分割/连接值。