This question already has answers here:
How to concatenate text from multiple rows into a single text string in SQL Server
(47 answers)
Closed 5 years ago.
I'm looking for a way to group whilst also concatenating rows into a comma separated string.
Example:
Name Place
Steve Jones New York
Steve Jones Washington
Albert Smith Miami
Albert Smith Denver
to ...
Steve Jones New York, Washington
Albert Smith Miami, Denver
Greatly appreciated
If you use SQL Server 2008 and above, you can use STUFF and XML PATH to get the result you want.
SELECT DISTINCT Name
, STUFF((
SELECT ',' + Place
FROM YourTable t1
where t1.Name = t2.Name
FOR XML PATH('')
), 1, 1, '') AS Places
FROM YourTable t2
You can use String_Agg if you are using 2017, SQL Azure else you can query as below:
Select [Name],
Places = Stuff((Select ', '+Place from yourtable where [Name] = t.[Name] for xml path('')),1,2,'')
from yourtable
group by [Name]
There's probably a simpler way, but this works:
with N as
(
select name, count(*) over (partition by name order by name) c from PersonPlace
),
Q as
(
select J.name, J.place, cast(J.place as varchar) place_name, J.c cnt, 1 c from
(select distinct(name) from PersonPlace) K
cross apply
(
select top 1 P.*, N.c from PersonPlace P
inner join N on N.name = P.name
where P.name = K.name
) J
union all
select P.name, P.place, cast(Q.place + ', ' + P.place as varchar), Q.cnt, Q.c + 1
from PersonPlace P
inner join Q on Q.name = P.name and Q.c + 1 <= cnt and P.place <> Q.place
)
select Q.name, Q.place_name from Q where Q.c = Q.cnt
Results:
name place_name
-------------- ----------------------
Steve Jones New York, Washington
Albert Smith Denver, Miami
Rextest Demo
If the People and Places are actually separate tables with their own keys, then we can simply quite a bit.
Using CROSS APPLY and FOR XML PATH:
SELECT distinct PP.Name, SUBSTRING(A.Places, 0, LEN(A.Places)) AS CombinedPlaces
FROM PersonPlace PP
CROSS APPLY
(
SELECT place + ', '
FROM PersonPlace AS AP
WHERE AP.Name = PP.Name
FOR XML PATH('')
) A(places)
Related
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
I work with the AventureWorks2014 database in Microsoft SQL Server. I need to create a view that will show the CustomerID, the full name and the TOTAL amount sold to client through the web.
My problem is that I can't seem to get the values corresponding to a single customer add up so that a single customer answers to a single line in my result. This is the code I have, any help would be appreciated. I basically need to show the total amount sold to clients on the web.
if object_id('vTotalWebSalesPerCustomer', 'v') is not null
drop view vTotalWebSalesPerCustomer;
go
create view vTotalWebSalesPerCustomer
as
select distinct
c.CustomerID,
ltrim(rtrim(concat(concat(concat(concat(concat(concat(concat(p.Title, ' '), p.LastName), ', '), ' '), p.FirstName), ' '), p.Suffix))) as NomClient,
soh.TotalDue
from
[Sales].[Customer] as c
left join
[Person].[Person] as p on c.CustomerID = p.BusinessEntityID
left join
[Sales].[SalesOrderHeader] as soh on soh.CustomerID = c.CustomerID
where
year(soh.OrderDate) = 2014
and datepart(quarter, soh.OrderDate) = 1
and [OnlineOrderFlag] = 1
go
select *
from vTotalWebSalesPerCustomer
Thanks
Sounds like you need to use GROUP BY and SUM(), example:
SELECT column-names
FROM table-name
WHERE condition
GROUP BY column-names
ORDER BY column-names
Such as:
SELECT SUM(O.TotalPrice), C.FirstName, C.LastName
FROM [Order] O JOIN Customer C
ON O.CustomerId = C.Id
GROUP BY C.FirstName, C.LastName
ORDER BY SUM(O.TotalPrice) DESC
Would return:
Sum FirstName LastName
117483.39 Horst Kloss
115673.39 Jose Pavarotti
113236.68 Roland Mendel
57317.39 Patricia McKenna
52245.90 Paula Wilson
34101.15 Mario Pontes
32555.55 Maria Larsson
DISTINCT filters result rows to remove duplicates. What I think you want is to aggregate rows. Perhaps this will do what you want:
create view vTotalWebSalesPerCustomer as
select c.CustomerID,
ltrim(rtrim(concat(concat(concat(concat(concat(concat(concat(p.Title, ' '), p.LastName), ', '), ' '), p.FirstName), ' '), p.Suffix))) as NomClient,
sum(soh.TotalDue) as TotalDue
from [Sales].[Customer] as c
left join [Person].[Person] as p on c.CustomerID = p.BusinessEntityID
left join [Sales].[SalesOrderHeader] as soh on soh.CustomerID = c.CustomerID
where year(soh.OrderDate) = 2014 and datepart(quarter, soh.OrderDate)=1 and [OnlineOrderFlag] = 1
group by c.CustomerID,NomClient
Note that I removed distinct, added sum operator on the amount you want to total, and group by the customer id and name fields (SQL Server requires that you list in the GROUP BY all result columns which are not being aggregated).
You can use the following VIEW using a GROUP BY on the SELECT:
IF OBJECT_ID('vTotalWebSalesPerCustomer', 'v') IS NOT NULL
DROP VIEW vTotalWebSalesPerCustomer;
GO
CREATE VIEW vTotalWebSalesPerCustomer AS
SELECT
x.CustomerID,
LTRIM(RTRIM(CONCAT(p.Title, ' ', p.LastName, ', ', p.FirstName, ' ', p.Suffix))) AS NomClient,
x.TotalDue
FROM (
SELECT
c.CustomerID AS CustomerID,
SUM(soh.TotalDue) AS TotalDue
FROM [Sales].[Customer] AS c
LEFT JOIN [Sales].[SalesOrderHeader] AS soh ON soh.CustomerID = c.CustomerID
WHERE YEAR(soh.OrderDate) = 2014 AND DATEPART(quarter, soh.OrderDate) = 1 AND [OnlineOrderFlag] = 1
GROUP BY c.CustomerID
)x LEFT JOIN [Person].[Person] AS p ON x.CustomerID = p.BusinessEntityID
GO
Note: You only need one CONCAT function to concat all string values.
For the first ID which is 1265 I want only one record like this(look below), and for now I have three different records for the same ID and that isn't good. You can see that in the first record with ID = 1265 I would like to have concated strings into one separated with ',':
1265 MARK, LISA, MAY
Here is my code sample:
select
vup.UgovorId as ID,
concat(p.Naziv, p.Naziv) as NUMBER
from
[TEST_Ugovori_Prod].dbo.VezaUgovorPartner as vup
inner join
[TEST_MaticniPodaci2].[dbo].[Partner] as p on vup.PartnerId = p.PartnerID
group by
vup.UgovorId, p.Naziv
order by
vup.UgovorId desc
Here are my results:
1265 MARK
1265 LISA
1265 MAY
1264 LINDA
1263 MARTINA
1262 MARKO
1261 VIDAKS
1260 PEEKS
1259 MARCUS
1258 MARKO
1257 MATIA
1256 POPOVIC
SELECT ID, NUMBER =
STUFF((SELECT DISTINCT ', ' + NUMBER
FROM example t2
WHERE t2.ID = t1.ID
FOR XML PATH('')), 1, 2, '')
FROM example t1
GROUP BY ID
ORDER BY ID DESC
[DEMO HERE]
If you are having trouble blending with your query above, this should help:
SELECT vup.UgovorId as ID,
STUFF((SELECT DISTINCT ', ' + p2.Naziv
FROM [TEST_Ugovori_Prod].dbo.VezaUgovorPartner vup2
INNER JOIN [TEST_MaticniPodaci2].[dbo].[Partner] p2 ON vup2.PartnerId = p2.PartnerID
WHERE vup2.UgovorId = vup.UgovorId
FOR XML PATH('')), 1, 2, '') NUMBER
FROM [TEST_Ugovori_Prod].dbo.VezaUgovorPartner vup
INNER JOIN [TEST_MaticniPodaci2].[dbo].[Partner] p on vup.PartnerId = p.PartnerID
GROUP BY vup.UgovorId
ORDER BY vup.UgovorId DESC
[Test blend]
I designed an EAV table that looks like this:
SID AID VID
1 1 1
1 2 1
1 3 2
1 4 3
1 1 2
SID stand for Subject ID, AID stands for Attribute ID and VID stands for ValuedID
also a table to map the attributes:
AttributeID AttributeName
1 Hobbies
2 Name
3 Gender
4 IrisColor
After using pivot on the first table, linked to the attribute table:
SELECT
SubjectID,
Hobbies,
Name,
Gender,
IrisColor
FROM
(
SELECT SubjectID, attr.AttributeName as attribute, ValueID from SubjectDetails, SubjectAttributes as attr WHERE SubjectDetails.AttributeID=attr.ID
) as t
PIVOT(
MAX(ValueID)
FOR attribute IN (Hobbies,Name,Gender,IrisColor)) AS t1
WHERE SubjectID=1
I get this:
SubjectID Hobbies Name Gender IrisColor
1 1 1 2 3
Which is almost correct, but SubjectAttribute 1 (which is hobbies) appears one more time in the first table (SubjectDetails), so what I want to achieve is this:
SubjectID Hobbies Name Gender IrisColor
1 **1,2** 1 2 3
I have to mention that I don't care about what separator is used and that I tried doing that with the STUFF function but it is a pain to combine PIVOT and STUFF (or I just don't know how).. Any suggestions?
This should work, I did the following:
Stored the information from your EAV table (table1) as single row per SID into a temporary table (you can create a view instead).
Then pivoted that resultset as below (using your pivot query):
SELECT *
FROM
(
SELECT * from #temptbl
) as t
PIVOT( MAX(vid) FOR attrname IN (Hobbies,Name,Gender,IrisColor)) AS t1
WHERE sid=1
I got this result:
Please check the full working version here.
This would work without using PIVOT, I think here no need to use PIVOT
SELECT SubjectID
,+STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Hobbies'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Hobbies
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Name'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Name
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Gender'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Gender
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'IrisColor'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS IrisColor
FROM #T_SubjectDetails H
GROUP BY SubjectID
I have data like below
| Name | Subject | Answer |
|:----------|:----------|:---------------------:|
|Pranesh |Physics |Numerical Problems |
|Pranesh |Physics |Other |
|Pranesh |Chemistry |Understanding Concepts |
|Pranesh |Chemistry |Organic chemistry reactions
|Pranesh |Maths |Lack of understanding |
|Pranesh |Maths |Insufficient practice |
|Pranesh |Maths |Other |
Using this SQL query:
select *
from
(select
l.FullName Name, sq.Title Subject, cAns.Name Answer
from
Answer as sa
left join
Question AS sq on sq.ID = sa.QuestionID
left join
Master as cAns on cAns.ID = sa.AnswerID
left join
Profile as l on l.ID = sa.ProfileID) src
pivot
(max(Answer)
for Subject in ([Physics], [Chemistry], [Maths], [Biology],[ComputerScience], [CommonUnderstanding])) piv;
I am able to get the data as follows
How to concatenate answer column of same subject and display the same like in above screen shot ?
I have concatenated the answer list first per name and subject, then applied pivoting -
declare #temp table (name varchar(100), subject varchar(100), answer varchar(100))
insert into #temp
select 'Pranesh','Physics' ,'Numerical Problems'
union all select 'Pranesh','Physics' ,'Other'
union all select 'Pranesh','Chemistry','Understanding Concepts'
union all select 'Pranesh','Chemistry','Organic chemistry reactions'
union all select 'Pranesh','Maths' ,'Lack of understanding'
union all select 'Pranesh','Maths' ,'Insufficient practice'
union all select 'Pranesh','Maths' ,'Other'
union all select 'Ramesh','Biology' , 'Other'
union all select 'Ramesh','Biology' , 'Science'
;with cte as (select distinct name, subject from #temp)
select * from
(
select
c.name,
c.subject,
answer = stuff((select ',' + answer from #temp t where t.name=c.name and t.subject=c.subject for xml path('')), 1, 1, '')
from cte c
) src
pivot
(
max(answer) for subject in ([Physics], [Chemistry], [Maths], [Biology],[ComputerScience],[CommonUnderstanding])
) piv
Try this:-
SELECT ANS, PHYSICS,CHEMISTRY,MATHS
FROM
(
SELECT L.FULLNAME NAME, SQ.TITLE SUBJECT, CANS.NAME ANSWER FROM ANSWER AS SA
LEFT JOIN QUESTION AS SQ ON SQ.ID = SA.QUESTIONID
LEFT JOIN MASTER AS CANS ON CANS.ID = SA.ANSWERID
LEFT JOIN PROFILE AS L ON L.ID = SA.PROFILEID
) AS T
UNPIVOT
(
COMBINE_COLUMN_VALUES FOR ANS IN (ANSWER)
) AS U
PIVOT
(
MAX(COMBINE_COLUMN_VALUES) FOR [SUBJECT] IN (PHYSICS,CHEMISTRY,MATHS)
) AS P