Displaying multiple row values in one row in SQL Server [duplicate] - sql

This question already has answers here:
Simulating group_concat MySQL function in Microsoft SQL Server 2005?
(12 answers)
Concatenate one field after GROUP BY
(1 answer)
Closed 8 years ago.
I have a table as follow:
ID User Activity PageURL
1 Me act1 ab
2 Me act1 cd
3 You act2 xy
4 You act2 st
I want to group by User and Activity such that I end up with something like:
User Activity PageURL
Me act1 ab, cd
You act2 xy, st
As you can see, the column PageURL is combined together separated by a comma based on the group by.
Would really appreciate any pointers and advice.

SELECT
[User], Activity,
STUFF(
(SELECT DISTINCT ',' + PageURL
FROM TableName
WHERE [User] = a.[User] AND Activity = a.Activity
FOR XML PATH (''))
, 1, 1, '') AS URLList
FROM TableName AS a
GROUP BY [User], Activity
SQLFiddle Demo

A good question. Should tell you it took some time to crack this one. Here is my result.
DECLARE #TABLE TABLE
(
ID INT,
USERS VARCHAR(10),
ACTIVITY VARCHAR(10),
PAGEURL VARCHAR(10)
)
INSERT INTO #TABLE
VALUES (1, 'Me', 'act1', 'ab'),
(2, 'Me', 'act1', 'cd'),
(3, 'You', 'act2', 'xy'),
(4, 'You', 'act2', 'st')
SELECT T1.USERS, T1.ACTIVITY,
STUFF(
(
SELECT ',' + T2.PAGEURL
FROM #TABLE T2
WHERE T1.USERS = T2.USERS
FOR XML PATH ('')
),1,1,'')
FROM #TABLE T1
GROUP BY T1.USERS, T1.ACTIVITY

Related

How to create a query to join multiple rows from a different table into single comma delimated column in SQL Server [duplicate]

This question already has answers here:
Simulating group_concat MySQL function in Microsoft SQL Server 2005?
(12 answers)
Closed 4 years ago.
I am using SQL Server 2014, and I have two tables:
number id
------------------
36-23 1
36-23 2
id value
------------------
1 asia
2 europe
The number column is of type varchar. I want to write a query to return the following results:
number Name
---------------------------
36-23 asia,europe
I am wondering how can I do this with the help of query or functions in SQL Server.
I think using STUFF is the easiest way to go forward here.
CREATE TABLE tableID
([number] varchar(50), [id] int)
;
INSERT INTO tableID
([number], [id])
VALUES
('36-23', 1),
('36-23', 2)
;
CREATE TABLE tableLoc
([id] int, [value] varchar(50))
;
INSERT INTO tableLoc
([id], [value])
VALUES
(1, 'asia'),
(2, 'europe')
;
SELECT tableID.number, tableLoc.value INTO temp1
FROM tableID INNER JOIN tableLoc ON tableID.id = tableLoc.id;
SELECT *, STUFF((
SELECT DISTINCT ', ' + value
FROM temp1
WHERE number = t.number
FOR XML PATH('')), 1, 2, '')
FROM (
SELECT DISTINCT number
FROM temp1
) t

T-SQL loop through two tables

I have been unable to find a working solution for below dilemma.
I am using SQL Server 2016 and have the 2 tables shown below in a database.
Users table:
Id Name
----------
1 Lisa
2 Paul
3 John
4 Mike
5 Tom
Role table:
Id UserId Role
------------------------
1 3 Manager
2 2,4,5 Developer
3 1 Designer
I am looking for T-SQL code that loops through the Role table, extracts UserIds and retrieves associated name for each Id from the Users table.
So the looped result would look like this:
John
Paul,Mike,Tom
Lisa
FOR SQL SERVER 2017
SELECT R1.Id,STRING_AGG(U.Name , ','),R1.Role
FROM Users U
INNER JOIN
(
SELECT R.Id,S.value AS UserId,R.Role
FROM Role R
CROSS APPLY STRING_SPLIT (UserID, ',') S
) R1
ON U.Id=R1.UserId
GROUP BY R1.ID,R1.Role
ORDER BY R1.ID;
OR
FOR SQL SERVER 2016
WITH CTE AS
(
SELECT R2.ID,U.Name,R2.UserId,R2.Role
FROM Users U
INNER JOIN
(
SELECT R.Id,S.value AS UserId,R.Role
FROM Role R
CROSS APPLY STRING_SPLIT (UserID, ',') S
)R2
ON U.id=R2.UserId
)
SELECT DISTINCT R1.Id,
STUFF((
SELECT ',' + name
FROM CTE R3
WHERE R1.Role = R3.Role
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS NAME
,R1.Role
FROM CTE AS R1;
OR
For Old versions
With CTE AS
(
SELECT r.id,
u.name,
r.Role
FROM Users u
INNER JOIN Role r
ON ',' + CAST(r.Userid AS NVARCHAR(20)) + ',' like '%,' + CAST(u.id AS NVARCHAR(20)) + ',%'
)
SELECT DISTINCT id,
STUFF((
SELECT ',' + name
FROM CTE md
WHERE T.Role = md.Role
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS NAME,
Role
FROM
CTE AS T
ORDER BY id
Output
id NAME Role
1 John Manager
2 Paul,Mike,Tom Developer
3 Lisa Designer
Demo
http://sqlfiddle.com/#!18/04a2d/69
There are several problems going on here. The first issue is that you're storing your values in a delimited format. Next, because you're storing your values in a delimited format, the values are being stored as a varchar. This has problems as well, as, as I would guess that the value of your column Id in the table Users is an int; meaning an implicit cast is needed and ruining any SARGability.
So, the solution is to fix the problem, in my view. Because you have a many to many relationship, you'll need an extra table. Let's design the tables as you have them right now, anyway:
CREATE TABLE Users (Id int, Name varchar(100));
CREATE TABLE Role (Id int, UserId varchar(100), [Role] varchar(100));
INSERT INTO Users
VALUES (1,'Lisa'),
(2,'Paul'),
(3,'John'),
(4,'Mike'),
(5,'Tom');
INSERT INTO Roles
VALUES(1,'3','Manager'),
(2,'2,4,5','Developer'),
(3,'1','Designer');
Now, instead we need a new table:
CREATE TABLE UserRoles (Id int, UserID int, RoleID int);
Now, we can insert the proper rows into the database. As you're using SQL Server 2016, we can use STRING_SPLIT:
INSERT INTO UserRoles (UserID, RoleID)
SELECT SS.value, R.Id
FROM Roles R
CROSS APPLY STRING_SPLIT (UserID, ',') SS;
After this, if you want, you could drop your existing column using the following, however, I see no harm in leaving it at the moment:
ALTER TABLE Roles DROP COLUMN UserID;
Now, we can query the data correctly:
SELECT *
FROM Users U
JOIN UserRoles UR ON U.ID = UR.UserID
JOIN Roles R ON UR.RoleID = R.Id;
If you want to then delimit this data, you can use STUFF, but don't store it back; I've explained how to correct your data for a reason! :)
SELECT [Role],
STUFF((SELECT ',' + [Name]
FROM Users U
JOIN UserRoles UR ON U.Id = UR.UserID
WHERE UR.RoleID = R.Id
FOR XML PATH ('')),1,1,'') AS Users
FROM Roles R;
If you were using SQL Server 2017, you'd be able to use STRING_AGG
Clean up script:
DROP TABLE UserRoles;
DROP TABLE Users;
DROP TABLE Roles;
Try this solution:
declare #users table (Id int, Name varchar(100))
declare #role table (Id int, UserId varchar(100), [Role] varchar(100))
insert into #users values
(1, 'Lisa'),
(2, 'Paul'),
(3, 'John'),
(4, 'Mike'),
(5, 'Tom')
insert into #role values
(1, '3', 'Manager'),
(2, '2,4,5', 'Developer'),
(3, '1', 'Designer')
select * from #role [r]
join #users [u] on
CHARINDEX(',' + cast([u].Id as varchar(3)) + ',', ',' + [r].UserId + ',', 1) > 0
I joined both tables based on occurence Id in UserId. To make it possible and avoid matches like: 2 is matched to 12, I decided to match only IDs surrounded by commas. That's why I wrapped in commas Id in a query and also wrapped UserId in commas, to match IDs at the end and the beginning of userId.
This query should give you satisfying result, but to match your desired output exatcly, you have to wrap this query in a CTE and perform group by with string concatenation:
;with cte as (
select [r].Id, [r].Role, [u].Name from #role [r]
join #users [u] on
CHARINDEX(',' + cast([u].Id as varchar(3)) + ',', ',' + [r].UserId + ',', 1) > 0
)
select Id,
(select Name + ',' from cte where Id = [c].Id for xml path('')) [Name],
--I believe this should work in your case, if so, just pick one column from these two
string_agg(Name + ',') [Name2],
Role
from cte [c]
group by Id, Role

How to concatenate many rows with same id in sql?

My table contains the details like with two fields:
ID DisplayName
1 Editor
1 Reviewer
7 EIC
7 Editor
7 Reviewer
7 Editor
19 EIC
19 Editor
19 Reviewer
I want get the unique details with DisplayName like
1 Editor,Reviewer 7 EIC,Editor,Reviewer
Don't get duplicate value with ID 7
How to combine DisplayName Details? How to write the Query?
In SQL-Server you can do it in the following:
QUERY
SELECT id, displayname =
STUFF((SELECT DISTINCT ', ' + displayname
FROM #t b
WHERE b.id = a.id
FOR XML PATH('')), 1, 2, '')
FROM #t a
GROUP BY id
TEST DATA
create table #t
(
id int,
displayname nvarchar(max)
)
insert into #t values
(1 ,'Editor')
,(1 ,'Reviewer')
,(7 ,'EIC')
,(7 ,'Editor')
,(7 ,'Reviewer')
,(7 ,'Editor')
,(19,'EIC')
,(19,'Editor')
,(19,'Reviewer')
OUTPUT
id displayname
1 Editor, Reviewer
7 Editor, EIC, Reviewer
19 Editor, EIC, Reviewer
DECLARE #t TABLE
(
ID INT,
DisplayName VARCHAR(50)
)
INSERT INTO #t (ID, DisplayName)
VALUES
(1 , 'Editor'),
(1 , 'Reviewer'),
(7 , 'EIC'),
(7 , 'Editor'),
(7 , 'Reviewer'),
(7 , 'Editor'),
(19, 'EIC'),
(19, 'Editor'),
(19, 'Reviewer')
SELECT *, STUFF((
SELECT DISTINCT ', ' + DisplayName
FROM #t
WHERE ID = t.ID
FOR XML PATH('')), 1, 2, '')
FROM (
SELECT DISTINCT ID
FROM #t
) t
Output -
----------- ------------------------
1 Editor, Reviewer
7 Editor, EIC, Reviewer
19 Editor, EIC, Reviewer
My post about string aggregation:
http://www.codeproject.com/Articles/691102/String-Aggregation-in-the-World-of-SQL-Server
For MySQL:
SELECT id, GROUP_CONCAT(displayname) FROM tableName GROUP BY id
Refer: http://www.sqlines.com/mysql/functions/group_concat
SQL Server 2017+ and SQL Azure: STRING_AGG
Starting with the next version of SQL Server, we can finally concatenate across rows without having to resort to any variable or XML witchery.
STRING_AGG (Transact-SQL)
SELECT ID, STRING_AGG(DisplayName, ', ') AS DisplayNames
FROM TableName
GROUP BY ID
and in case of oracle database
select id,
listagg(displayname, ',') within group (order by displayname) as names
from test
group by id
to change the separator use
SELECT id, GROUP_CONCAT(displayname SEPARATOR ';') FROM tableName GROUP BY id
this will change separator from comma to semicolon :)
Thank you all,
SELECT Distinct
t1.ID,
MAX(STUFF(t2.x_id,1,1,'')) AS DisplayName
FROM Table t1
CROSS apply(
SELECT Distinct ', ' + SUBSTRING(t2.DisplayName,1,2)
FROM Table t2
WHERE t2.ID = t1.ID AND t2.DisplayName > ''
FOR xml PATH('')
) AS t2 (x_id)
GROUP BY
t1.ID
order by 1
GO

SQL Server Row to Cols [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
SQL Server: Examples of PIVOTing String data
(7 answers)
Closed 7 years ago.
I seems an simple task but I need some help from the sql experts with this one.
The number of contacts can vary from 1 to 30 persons and I want all the phone numbers in one row per cust_ref.
How can I do this??
This is a solution without dynamic sql, please try out:
declare #contacts table(cust_ref int,phone varchar(100));
insert into #contacts values(10000,'ph10000a'),(10000,'ph10000b'),(10000,'ph10000c')
,(10001,'ph10001a'),(10001,'ph10001b'),(10001,'ph10001c')
,(10002,'ph10002a'),(10002,'ph10002b');
SELECT p.*
FROM
(
SELECT 'phone'+REPLACE(STR(ROW_NUMBER() OVER(PARTITION BY cust_ref ORDER BY phone),2),' ','0') AS inx,*
FROM #contacts
) AS tbl
PIVOT
(
MIN(phone) FOR inx IN(phone01,phone02,phone03,phone04) --type more...
) AS p
by using CROSS APPLY,CTE and UNION ALL we can get the above requirement
declare #tableA table (RefId int,Phone varchar(10))
insert into #tableA (RefId,Phone)values (10000,'030123456'),(100123,060123445)
Declare #tableB table (CustID INT,RefId INT,Phone varchar(10)))
insert into #tableB (CustID,RefId,Phone)values
(1,10000,'030245789'),
(2,10000,'030245889'),
(1,100123,'060245789'),
(2,100123,'060243389'),
(3,100123,'060289389')
; with CTE AS (
select A.RefId,A.Phone As Phone from #tableA A
UNION
select B.RefId,B.Phone As Phone from #tableB B )
select * from (
Select RefId,
val,
COL + CAST(ROW_NUMBER()OVER(PARTITION BY RefId ORDER BY RefId) AS VARCHAR(1))RN
FROM CTE
CROSS APPLY (VALUES ('Phone',Phone))CS(Col,val))T
PIVOT (MAX(VAL) FOR RN IN ([Phone1],[Phone2],[Phone3],[Phone4]))P

Build string from a SELECT statement [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server Group Concat with Different characters
I need an example on SELECT where the output is a ',' separated string
e.g. SELECT (... something ...) name AS output FROM name_table
gives me
output
-----------------------------
'Ann', 'Tom', 'Wilson', .....
How would you do that in SQL server 2008 R2?
Thank you!
Assuiming you have a schema like this,
CREATE TABLE Table1
([GROUP_ID] int, [PERSON_NAME] varchar(6));
INSERT INTO Table1
([GROUP_ID], [PERSON_NAME])
VALUES
(1001, 'ALEX'),
(1002, 'MATHEW'),
(1001, 'GEORGE'),
(1002, 'THOMAS'),
(1001, 'JAMES');
create a query something like this to produce a comma separated value,
SELECT
GROUP_ID,
STUFF(
(SELECT ', ' + PERSON_NAME
FROM Table1
WHERE [GROUP_ID] = a.GROUP_ID
FOR XML PATH (''))
, 1, 1, '') AS NamesList
FROM Table1 AS a
GROUP BY GROUP_ID
SQLFiddle Demo