Merging results from SQL Query, where most values are the same [duplicate] - sql-server

This question already has answers here:
Simulating group_concat MySQL function in Microsoft SQL Server 2005?
(12 answers)
Closed 6 years ago.
Sorry for the not-so-great title.
I'm triyng to merge together sets of results to provide a quick view of some data.
If I have this query:
SELECT
tblPriv.ID, tblGroups.Name AS 'Group', tblPriv.User, tblPriv.Role
FROM
tblPriv
INNER JOIN
tblGroups on tblPriv.ID = tblGroups.ID
which returns these results:
ID GROUP USER ROLE
---------------------------------
1 Taxes DAVE Admin
1 Taxes JOHN Admin
1 Taxes BOB PowerUser
2 Catering RON Admin
2 Catering JACK PowerUser
2 Catering JIM PowerUser
(where ID is relational to say, a group, stored in another table)
What I ideally want to do is get 1 record for a group:
ID GROUP ADMINS POWERUSERS
---------------------------------------------
1 Taxes DAVE; JOHN; BOB
2 Catering RON; JACK; JIM;
I know the roles beforehand - they always stay the same, and new roles are not added, ever.
How would I go about doing this? (I will also include data from other relational tables)

SELECT
DISTINCT tp.ID,
tp.[GROUP],
ISNULL(STUFF(Admins.Admins, 1, 1, ''), '') AS Admins,
ISNULL(STUFF(PowerUsers.PowerUsers, 1, 1, ''), '') AS PowerUsers
FROM tlbPriv AS tp
OUTER APPLY
(
SELECT ' ' + tmp.[USER] + ';'
FROM tlbPriv AS tmp
WHERE tmp.ID = tp.ID AND [tmp].[ROLE] = 'Admin' FOR XML PATH('')
)Admins(Admins)
OUTER APPLY
(
SELECT ' ' + tmp.[USER] + ';'
FROM tlbPriv AS tmp
WHERE tmp.ID = tp.ID AND [tmp].[ROLE] = 'PowerUser' FOR XML PATH('')
)PowerUsers(PowerUsers)

For your particular problem:
select g.id, g.name,
stuff((select '; ' + p.user
from tblPriv p
where p.id = g.id and p.role = 'Admin'
for xml path ('') type
).value('', 'varchar(max)'
), 1, 2, ''
) as admins,
stuff((select '; ' + p.user
from tblPriv p
where p.id = g.id and p.role = 'PowerUser'
for xml path ('') type
).value('', 'varchar(max)'
), 1, 2, ''
) as PowerUsers
from tblGroups g

Tagged tsql, I assume this is for MS SQL server:
DECLARE #users TABLE
(
[ID] INT ,
[GROUP] VARCHAR(8) ,
[USER] VARCHAR(4) ,
[ROLE] VARCHAR(9)
);
INSERT INTO #users
( [ID], [GROUP], [USER], [ROLE] )
VALUES ( 1, 'Taxes', 'DAVE', 'Admin' ),
( 1, 'Taxes', 'JOHN', 'Admin' ),
( 1, 'Taxes', 'BOB', 'PowerUser' ),
( 2, 'Catering', 'RON', 'Admin' ),
( 2, 'Catering', 'JACK', 'PowerUser' ),
( 2, 'Catering', 'JIM', 'PowerUser' );
WITH data ( ID, [GROUP], [ROLE], [MEMBERS] )
AS ( SELECT u.ID ,
u.[GROUP] ,
u.ROLE ,
( SELECT [USER] + ';'
FROM #users AS [u1]
WHERE u.ID = u1.ID AND u.[GROUP] = u1.[GROUP] and u.ROLE = u1.ROLE
FOR
XML PATH('')
)
FROM #users AS [u]
)
SELECT ID ,
[GROUP] ,
[Admin] AS [Admins] ,
[PowerUser] AS [POWERUSERS]
FROM data PIVOT ( MAX(MEMBERS) FOR [ROLE] IN ( [Admin], [PowerUser] ) ) pvt;

In MYSQL Use group concat
SELECT
tblPriv.ID
, tblGroups.Name AS 'Group'
, group_concat(t1.User) AS ADMINS, group_concat(t2.User) AS POWERUSERS
FROM tblPriv
INNER JOIN tblGroups as t1 on t1.ID = tblGroups.ID and t1.user='ADMIN'
INNER JOIN tblGroups as t2 on t2.ID = tblGroups.ID and t1.user='POWERUSER'
GROUP BY tblPriv.ID

Related

SQL Server - Concatenate 3 different tables matching correspondent ID's of those columns

I've been on this for hours trying to figure this one out. So I need to concatenate values in a single column based on its ID's.
I have 3 tables named Type, DeliverType and Platform. I need to get all the matching ID's in the table Platform and concatenate all of its values in another column of table Platform.
Example :
Table Type Table DeliverType Table Platform
----------------------------------------------------------------------------------------
ID | Type ID | DeliverType ID | TypeID | DeliverType
----------------------------------------------------------------------------------------
1 | TestType1 1 | DeliverType1 9 | 3 | 1
2 | TestType2 2 | DeliverType2 9 | 4 | 2
3 | TestType3 3 | DeliverType3 23 | 2 | 4
4 | TestType4 4 | DeliverType4 23 | 1 | 3
These are my tables , I want to return the name of Type, the name of DeliverType and the ID of Platform
Expected Result
Table Platform
ID | NamesConcat
9 | TestType1,TestType2,TestType3,DeliverType1,DeliverType2,9
23 | TestType2,TesType3,TestType4,DeliverType3,DeliverType4,23
So it has only 1 ID which is 9 and all of the testTypes and DeliverTypes correspondant to that ID in table platform needs to be concat.
Here's my code so far:
SELECT DISTINCT p.ID,
SUBSTRING(
(
SELECT ','+ d.DeliverType,a.Type AS [text()]
FROM dbo.DeliverType d
INNER JOIN dbo.Type a ON a.ID = p.TypeID
ORDER BY d.ID
FOR XML PATH ('')
), 2, 1000) [NamesConcat]
FROM dbo.Platform p
Would appreciate some help on this one fellow programmers.
Thank you very much in advance.
From the sample data you show, I don't get the same expected result. I would expect the result:
Table Platform
ID
NamesConcat
9
TestType3,TestType4,DeliverType1,DeliverType2,9
23
TestType1,TestType2,DeliverType3,DeliverType4,23
If that is indeed the expected result and you want the strings concatenated by the Type first and then by DeliverType you can do that with this query:
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM Platform p
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
If you don't need the concatenated values in that order, you could do it with a shorter query:
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
So that you can check the work, here is a simple script that I wrote to mimic your demo data and the result:
DECLARE #Type AS TABLE (
ID INT IDENTITY(1,1) NOT NULL,
[Type] VARCHAR(20) NOT NULL
);
INSERT INTO #Type([Type]) VALUES('TestType1'), ('TestType2'), ('TestType3'), ('TestType4');
DECLARE #DeliverType AS TABLE (
ID INT IDENTITY(1, 1) NOT NULL,
[DeliverType] VARCHAR(25) NOT NULL
);
INSERT INTO #DeliverType([DeliverType]) VALUES ('DeliverType1'), ('DeliverType2'), ('DeliverType3'), ('DeliverType4');
DECLARE #Platform AS TABLE (
ID INT NOT NULL,
TypeID INT NOT NULL,
DeliverTypeID INT NOT NULL
);
INSERT INTO #Platform(ID, TypeID, DeliverTypeID) VALUES (9, 3, 1), (9, 4, 2), (23, 2, 4), (23, 1, 3);
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM #Platform p
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
I hope this is what you were looking for!

T-SQL query in SQL Server

I want to write a query where this should meet the condition shown in the first screenshot. Currently, I am getting second screenshot data which is not correct. I am having a hard time achieving this.
SELECT
CONCAT(p.[prefNameFamilyName], ' ', p.[prefNameGivenName], ' ', p.[prefNameMiddleNames], ' ', pre.code, ' ', nt.code) AS PatientName,
CONCAT(pn.familyName, ' ', pn.givenName, ' ', pn.middleNames, ' ', nt.code) AS PatientAlias,
CONCAT(pa.streetAddressLine1, ' ', pc.code, ' ', sp.code, ' ', ap.code, ' ', ac.code, ' ', atp.code) AS PatientAddress
FROM
identifier.identifier I
INNER JOIN
[Patient].[Patient] p ON i.id = p.patientIdentifierId
INNER JOIN
[Patient].PatientName pn ON pn.patientId = p.id
INNER JOIN
[Identifier].[LK_NamePrefix] pre ON pre.id = p.prefNamePrefixId
INNER JOIN
[Identifier].[LK_NameType] nt ON nt.id = pn.nameTypeId
LEFT JOIN
Patient.PatientAddress a ON a.patientId = p.id
INNER JOIN
[Patient].[PatientAddress] pa ON pa.patientId = p.id
INNER JOIN
[Address].[City] pc ON pa.cityId = pc.id
LEFT JOIN
[Address].[Country] ac ON ac.id = pa.countryId
LEFT JOIN
[Address].[StateOrProvince] sp ON sp.id = pa.stateOrProvinceId
LEFT JOIN
[Address].[Postcode] ap ON ap.id = pa.postcodeId
LEFT JOIN
[Address].[AddressType] atp ON atp.id = pa.addressTypeId
WHERE
I.identifier = 'ABC123'
You could use common table expressions (CTE's) to isolate the joins. This solution generates row numbers for the different combinations and joins on the row numbers later on. The final step is to filter out the rows with only null values. This solution works for any combination of a name, one or more aliases and one or more addresses.
Sample data
declare #names table
(
id int,
name nvarchar(20)
);
insert into #names (id, name) values
(1, 'Bruce Wayne'),
(2, 'Peter Parker'),
(3, 'Clark Kent');
declare #aliases table
(
id int,
alias nvarchar(20)
)
insert into #aliases (id, alias) values
(1, 'Batman'),
(1, 'The bat'),
(1, 'Batty'),
(2, 'Spider-Man'),
(2, 'Spidey'),
(3, 'Superman');
declare #addresses table
(
id int,
address nvarchar(20)
);
insert into #addresses (id, address) values
(1, 'Wayne Manor'),
(1, 'The Cave'),
(2, 'Aunt May''s house'),
(3, 'The Daily Planet'),
(3, 'Krypton');
Solution
with cte_na as
(
select na.id,
na.name,
row_number() over(partition by na.id order by na.name) as nameNum
from #names na
left join #aliases al
on al.id = na.id
left join #addresses ad
on ad.id = na.id
),
cte_al as
(
select al.id,
al.alias,
row_number() over(partition by al.id order by al.alias) as aliasNum
from #aliases al
),
cte_ad as
(
select ad.id,
ad.address,
row_number() over(partition by ad.id order by ad.address) as addressNum
from #addresses ad
)
select cna.id,
cna.name,
coalesce(cal.alias, '') as alias,
coalesce(cad.address, '') as address
from cte_na cna
left join cte_al cal
on cal.id = cna.id
and cal.aliasNum = cna.nameNum
left join cte_ad cad
on cad.id = cna.id
and cad.addressNum = cna.nameNum
where not (cal.id is null and cad.id is null);
Result
id name alias address
----------- -------------------- -------------------- --------------------
1 Bruce Wayne Batman The Cave
1 Bruce Wayne Batty Wayne Manor
1 Bruce Wayne The bat
2 Peter Parker Spider-Man Aunt May's house
2 Peter Parker Spidey
3 Clark Kent Superman Krypton
3 Clark Kent The Daily Planet

Select all users from subgroups in T-SQL query [duplicate]

This question already has an answer here:
SQL Server recursive self join
(1 answer)
Closed 3 years ago.
I have a SQL Server table that holds groups and subgroups that I am using to define user permissions in application. What I am trying to accomplish is to select all users ids from all subgroups based on used id assigned to group.
I have tried with this query but it only shows one level of subgroup users.
SET #UserIds =
ISNULL(
STUFF((SELECT ',' + CAST(tbl.UserID AS VARCHAR(MAX))
FROM
(SELECT grpUser.UserId AS UserID
FROM dbo.GroupUser grpUser
INNER JOIN (SELECT subGrp.Id, subGrp.GroupName
FROM dbo.Groups grp
INNER JOIN dbo.GroupUser guser ON grp.Id = guser.GroupId
AND guser.UserId = #UserId
INNER JOIN dbo.Groups subGrp ON grp.Id = subGrp.ParentGroupId) tbl ON grpUser.GroupId = tbl.Id) AS tbl
FOR XML PATH('')), 1, 1, ''), '')
I am struggling to get users ids in all subgroups. Number of subgroups is not defined. Is there a way to do in sql or should I shift this part to application side? Thank you for any advice.
Finally I came up with this code. Thank you for hint.
declare #UserGroupId int;
Set #UserGroupId = (Select GroupId from GroupUser gu
left join dbo.Groups gr on gu.GroupId = gr.id
where UserId = #UserId and gr.IsDeleted = 0)
declare #UsersGroups Table (
Id int not null,
GroupName nvarchar(50) not null
)
;WITH grp AS
(
SELECT Id, GroupName
FROM Groups
WHERE ParentGroupId = 1
UNION ALL
SELECT g.Id, g.GroupName
FROM Groups g
INNER JOIN dbo.GroupUser guser ON g.Id = guser.GroupId
INNER JOIN grp ON g.ParentGroupId = grp.Id
)
Insert into #UsersGroups
SELECT Id, GroupName
FROM grp
SET #UserIds = ISNULL(STUFF(( SELECT ',' + CAST(tbl.UserID AS VARCHAR(max)) FROM (
SELECT grpUser.UserId AS UserID FROM dbo.GroupUser grpUser
INNER JOIN ( SELECT * FROM #UsersGroups
) tbl ON grpUser.GroupId = tbl.Id
) AS tbl FOR XML PATH('') ), 1, 1, ''), '')

SQL - Comma Delimited subset results of multiple column in its own column

I have a view with a union all table, a person may belong to one or many table.
how do i create a query that will add a column with a ';' delimited where the person belong to, the ID is unique per person.
here's the example
--table1
PID fName tableMem
1 test group1
2 test2 group1
--table2
PID fName tableMem
1 test group2
3 test3 group2
--table3
PID fName tableMem
1 test group3
3 test3 group3
Here's the output I wanted
--compiled table after union of all the 3 tables
PID fname tableMem
1 test group1;group2;group3
2 test2 group1
3 test3 group2;group3
Here's the query I built from reading here for the past 2 days.I'm using STUFF and partition because I need the row to be distinct and this query will run as view.
SELECT *
FROM
(SELECT
*,
ROW_NUMBER() OVER(PARTITION BY PIP ORDER BY Fname) AS rownum
FROM
(SELECT
*,
STUFF((SELECT ';'+ di.tablemem
FROM DPI di <<-- alias table from union
WHERE DPI.PID = di.PID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') tablemem
FROM
(SELECT *
FROM
(--table1
SELECT 'group1' AS tableMem, * FROM table1
UNION ALL
--table2
SELECT 'group2' AS tableMem, * FROM table2
UNION ALL
--table3
SELECT 'group3' AS tableMem, * FROM table3) AS DPI <<--alias table name
) AS innertable
) AS distinctTable
) AS outerTable
WHERE
rownum = 1
what am I missing or what is wrong with the query. I'm guessing its because Im using a derived table name of the union sub select. is there any workaround?
Thank you in advance
You need GROUP BY data by PID, fName, using the FOR XML aggregation it would be
WITH DPI AS (
--table1
Select 'group1' as tableMem,* from table1
UNION ALL
--table2
Select 'group2' as tableMem,* from table2
UNION ALL
--table3
Select 'group3' as tableMem,* from table3
)
SELECT PID, fName
, STUFF((
SELECT ';'+ di.tablemem
FROM DPI di
WHERE di.PID = di1.PID
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,'') tablemem
FROM DPI di1
GROUP BY PID, fName;
Here is a possible solution using CTE:
;with cte as (
select * from #tbl1
union all
select * from #tbl2
union all
select * from #tbl3
)
select distinct
pid
,fname
,stuff(
(select '; ' + tableMem
from cte
where pid = a.pid
and fname = a.fname
FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'') as tableMem
from cte a
Given that you have a finite number of tables, I think I find this simpler:
select p.pid, p.fname,
trim(';' from
(case when t1.pid is not null then 'group1;' else '' end) +
(case when t2.pid is not null then 'group2;' else '' end) +
(case when t3.pid is not null then 'group3;' else '' end)
) as groups
from (select pid, fname from table1 union -- on purpose to remove duplicates
select pid, fname from table2 union
select pid, fname from table3
) p left join
table1 t1
on t1.pid = p.pid left join
table2 t2
on t2.pid = p.pid left join
table3 t3
on t3.pid = p.pid;
The most recent version of SQL Server supports string_agg(), which would make this simpler still.
Try this:
DECLARE #table1 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
DECLARE #table2 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
DECLARE #table3 TABLE
(
[PID] TINYINT
,[fname] VARCHAR(12)
,[tableMem] VARCHAR(12)
);
INSERT INTO #table1 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group1')
,(2, 'test2', 'group1');
INSERT INTO #table2 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group2')
,(3, 'test3 ', 'group2');
INSERT INTO #table3 ([PID], [fname], [tableMem])
VALUES (1, 'test', 'group3')
,(3, 'test3 ', 'group3');
WITH DataSource AS
(
SELECT *
FROM #table1
UNION ALL
SELECT *
FROM #table2
UNION ALL
SELECT *
FROM #table3
)
SELECT DISTINCT DS.[PID]
,DS.fname
,CSVvalue.[tableMem]
FROM DataSource DS
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + DS1.[tableMem]
FROM DataSource DS1
WHERE DS.[PID] = DS1.[PID]
AND DS.[fname] = DS1.[fname]
ORDER BY DS1.[tableMem]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) CSVvalue ([tableMem]);

SQL Server: How to show list of manager name separated by comma

i have to show employee name and his manager name hierarchy separated by comma.
if ram is top manager and ram is sam manager and sam is rahim manager then i would like to have output like
Desired output
EMP Name Manager's Name
--------- ---------------
Rahim Sam,Ram
Sam Ram
Ram No manager
i got script which show the employee and his manager name. here is script
;WITH EmployeeCTE AS
(
Select ID,Name,MgrID, 0 as level FROM #Employee
WHERE ID=3
UNION ALL
Select r.ID,r.Name,r.MgrID, level+1 as level
FROM #Employee r
JOIN EmployeeCTE p on r.ID = p.MgrID
)
Select e1.Name
,ISNULL(e2.Name,'Top BOSS') as [Manager Name]
,row_number() over (order by e1.level desc) as [Level]
from EmployeeCTE e1
left join EmployeeCTE e2 on e1.MgrID=e2.ID
Output
Name Manager Name Level
Simon Top BOSS 1
Katie Simon 2
John Katie 3
i also know how to show comma separated list. here is one sample script.
SELECT
PNAME,
STUFF
(
(
SELECT ',' + Mname
FROM Myproducts M
WHERE M.PNAME = P.PNAME
ORDER BY Mname
FOR XML PATH('')
), 1, 1, ''
) AS Models
FROM
Myproducts p
GROUP BY PNAME
now some tell me how could i merge two script to get the desired output. thanks
CREATE TABLE #EMP (
EmpID INT
, ManagerID INT
, Name NVARCHAR(50) NULL
);
INSERT INTO #EMP (EmpID, ManagerID, Name)
VALUES
( 1, NULL, 'John')
, (2, 1, 'Katie')
, (3, 2, 'Simon');
SELECT *
FROM
#EMP;
WITH a AS (
SELECT
EmpID
, Name
, ManagerID
, CONVERT(NVARCHAR(MAX),'') AS ManagerChain
FROM
#Emp
WHERE
ManagerID IS NULL
UNION ALL
SELECT
e.EmpID
, e.Name
, e.ManagerID
, CASE
WHEN a.ManagerChain ='' THEN a.Name
ELSE CONCAT(a.Name, CONCAT(',',a.ManagerChain))
END
FROM
#Emp e
JOIN a ON e.ManagerID = a.EmpID
)
SELECT
a.Name
, IIF(a.ManagerChain='','No Manager',a.ManagerChain) AS ManagerChain
FROM
a;
DROP TABLE #EMP;
Assuming a table structure of
DECLARE #Employee TABLE(
ID INT,
Name VARCHAR(10),
MgrID INT)
INSERT INTO #Employee
VALUES (1,'Ram',NULL),
(2,'Sam',1),
(3,'Rahim',2);
You can use
WITH EmployeeCTE
AS (SELECT ID,
Name,
MgrID,
0 AS level,
CAST('No manager' AS VARCHAR(8000)) AS [Managers Name]
FROM #Employee
WHERE MgrID IS NULL
UNION ALL
SELECT r.ID,
r.Name,
r.MgrID,
level + 1 AS level,
CAST(P.Name + CASE
WHEN level > 0
THEN ',' + [Managers Name]
ELSE ''
END AS VARCHAR(8000))
FROM #Employee r
JOIN EmployeeCTE p
ON r.MgrID = p.ID)
SELECT *
FROM EmployeeCTE

Resources