Reputation: 1
I have a table Client with columns ClientID, Name
ClientID|Name
1 |A
2 |B
3 |C
4 |D
5 |E
A Users table with columns : 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
Currently all DeniedClients are set to NULL.
I need to update The Users table, something like :
Update users set DeniedClients= (All existing ClientIDs in Client table) –` (users.AllowedClients)
OutPut:
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
How to write this query without using cursor.
Upvotes: 0
Views: 224
Reputation: 1953
As other says, structure is bad ... but still there is a way to do it, this is how I would do it in T-SQL ...
EDIT: Fixed part of 1 matching with 11 ... more testing needed, but working on look
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
Upvotes: 1
Reputation: 43646
The output below is produced by the following code:
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)
You can now perform your update.
If this is a real project and there are many records it will be faster to normalized your data. If you are stuck to the CSV format because of other reasons, you should write some CLR functions for splitting/concatenating values.
Upvotes: 0