display more than one value using a SQL query - sql

I am trying to display multiple authors per title in a single column. At the moment there a repeating rows, due to the fact that some Titles have more than 1 FirstName. Is there a form of concatenation that can be used to resolve this and display all the authors in a single filed and perhaps separated by a comma.
This is my current query:
SELECT
Submission.Title, Researcher.FirstName, Submission.Type
FROM
Submission
INNER JOIN
((Faculty
INNER JOIN
School ON Faculty.FacultyID = School.[FacultyID])
INNER JOIN
(Researcher
INNER JOIN
ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID)
ON School.SchoolID = Researcher.SchoolID)
ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY
Submission.Title, Researcher.FirstName, Submission.Type;
This the output it generates:
[
this is the output I am trying to generate:
Title FirstName Type
---------------------------------------------------------------------------
21st Century Business Matthew, Teshar Book Chapter
A Family Tree... Keshant, Lawrence Book Chapter
Benefits of BPM... Jafta Journal Article
Business Innovation Matthew, Morna, Teshar Book Chapter

You may inclde the concantenation logic within a CROSS APPLY
SELECT
Submission.Title
, CA.FirstNames
, Submission.Type
FROM Submission
CROSS APPLY (
SELECT
STUFF((
SELECT /* DISTINCT ??? */
', ' + r.FirstName
FROM ResearcherSubmission rs
INNER JOIN Researcher r ON r.ResearcherID = rs.ResearcherID
WHERE Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')
) AS CA (FirstNames)
GROUP BY
Submission.Title
, CA.FirstNames
, Submission.Type
;
NB: I'm not sure if you need to include DISTINCT into the subquery when concatenating the names, e.g. if these was 'Jane' (Smith) and 'Jane' (Jones) do you want the final list as: 'Jane' or 'Jane, Jane'?

You can do this in your application logic as well.
But if you want to do this with a query. You should be able do something like this:
SELECT DISTINCT
sm.Title,
STUFF(
(SELECT ', ' + r.FirstName
FROM ResearcherSubmission rs
INNER JOIN Researcher r ON r.ResearcherID = rs.ResearcherID
WHERE sm.SubmissionID = rs.SubmissionID
FOR XML PATH('')), 1, 2, '') AS FirstNames,
sm.Type
FROM Submission sm

You can use the below query to generate the o/p as you want from the o/p that you have got.
CREATE TABLE #temptable(Title VARCHAR(200), FirstName VARCHAR(200), Type VARCHAR(200))
INSERT INTO #temptable
SELECT 'Book1','Matt','Chapter' UNION
SELECT 'Book1','Tesh','Chapter' UNION
SELECT 'BPM','Jafta','Article' UNION
SELECT 'Ethics','William','Journal' UNION
SELECT 'Ethics','Lawrence','Journal' UNION
SELECT 'Ethics','Vincent','Journal' UNION
SELECT 'Cellular','Jane','Conference'
SELECT Title
,STUFF((SELECT ', ' + CAST(FirstName AS VARCHAR(10)) [text()]
FROM #temptable
WHERE Title = t.Title
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
,Type
FROM #temptable t
GROUP BY Title,Type

Related

SQL Server - Concatenate - Stuff doesn't work

I have this query in SQL Server and I want to concatenate using stuff the 'Products' column, but I don't know why this doesn't work...
SELECT BrandsProvider.id, BrandsProvider.Name,
(SELECT (STUFF((
SELECT ','+ CONVERT(NVARCHAR(MAX), sku)
FROM items it2
WHERE it2.sku = it.sku
FOR XML PATH('')),
COUNT('ID'), 1, ''))) AS 'Products'
FROM items it
INNER JOIN BrandsProvider ON it.IdBrandProduct = BrandsProvider.id
And the result is:
Id Name Products
--------------------------------
1 BRAND EXAMPLE PR344
1 BRAND EXAMPLE PR345
And I want this:
Id Name Products
--------------------------------
1 BRAND EXAMPLE PR344, PR345
Also I used SELECT DISTINCT in the query but the result is the same...
So, where can be the mistake?
SELECT b.id
, b.Name
, STUFF((SELECT ', ' + CAST(i.sku AS VARCHAR(10)) [text()]
FROM items i
WHERE i.IdBrandProduct = b.id
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,'') Products
FROM BrandsProvider b
GROUP BY b.id
, b.Name
SQL Server 2017 and Later Versions
If you are working on SQL Server 2017 or later versions, you can use built-in SQL Server Function STRING_AGG to create the comma delimited list:
SELECT b.id
, b.Name
, STRING_AGG(i.sku, ', ') AS Products
FROM BrandsProvider b
INNER JOIN items i ON i.IdBrandProduct = b.id
GROUP BY b.id
, b.Name;

SELECT STUFF rewrite to Redshift

I have a SQL script, that I need to convert to redshift.
Here is the part, where I have a problem:
LEFT JOIN
(
SELECT STUFF((
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
) ,1,1,'') AS Services
) AS labelServices
I have read that I can use SELECT LISTAGG and try to use it like this:
LEFT JOIN
(
SELECT LISTAGG((
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
) ,1,1,'') AS Services
) AS labelServices
But it does not work.
So how I can rewrite it to be correct?
You don't need all the XML stuff. In fact, it is XML that is doing the aggregation in SQL Server, not STUFF(). STUFF() is just used for beautifying the string after it is created.
So, something like this:
LEFT JOIN
(SELECT cl.entityid, LISTAGG(clo.name, ', ') WITHIN GROUP (ORDER BY clo.name) as names
FROM public.label_entities cl JOIN
public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
GROUP BY cl.entityid
) AS labelServices
ON labelServices.entityid = clv.contactid
use replace() instead of stuff()
select STUFF(', hai, hello, fine', 1, 1, '')
select replace(','+', hai, hello, fine', ',,', '') --- ', hai, hello, fine' would be
--- returned by inner select
EDIT 1
select REPLACE(','+
(
SELECT ','+ clo.name
FROM public.label_entities cl
JOIN public.label_history clo
ON clo.id = cl.labelid
WHERE clo.parentid = 993
AND cl.entityid = clv.contactid
FOR XML PATH('')
)
,',,'
,''
)

How to get Email ID(one value) for each ID for 1st table and then display Email Name from 2nd table as comma Separated in SQL Server 2012?

What i have tried
Results wanted like thisFrom One table , for each ID there can be multiple email id's based on some condition Ex
ID EmailID's
1 Mike.Foster#Mail.com
1 lilly.Foster#Mail.com
2 Michel.Josh#Mail.com
2 Nash.Ted#Mail.com
I have to get email Name from these Ids from another table, something like this
Output i Need
Email_Name
Foster.Mike,Foster.Lilly
Josh.Michel,Ted.Nash
This is what i tried.
SELECT User_Email =
STUFF((SELECT DISTINCT ', ' + User_Email
FROM table1 b
WHERE b.Component_ID= a.Component_ID
and [Role] ='Team Lead' and Functional_Group ='Product'
FOR XML PATH('')), 1, 2, '')
FROM [WFS].table1 a
GROUP BY table1
Now another table i want Email Names
Select EmailNamefrom Table2 where EmailIDs IN ( 'code for Email')
Table1 schema
ID Component_ID EmailIDs Role Functional_Group
1 1 Mike.Foster#Mail.com Team Lead Product
2 1 lilly.Foster#Mail.com Team Lead Product
3 2 Michel.Josh#Mail.com Team Lead Product
4 2 Nash.Ted#Mail.com Team Lead Product
Table 2 schema
ID EmailIDs EmailName
1 Mike.Foster#Mail.com Foster.Mike
2 lilly.Foster#Mail.com Foster.Lilly
Any suggestions are welcome.Thanks in advance
Disregard this answer as I found out that GROUP_CONCAT() is a MySQL specific function, which means it won't work in SQL Server, however, I'll let it stay for future references.
SELECT
GROUP_CONCAT(EmailName SEPARATOR ', ') as name
FROM
table1
INNER JOIN table2 ON table1.EmailIDs=table2.EmailIDs
WHERE
table1.EmailIDs=table2.EmailIDs
GROUP BY
table1.Component_ID
Output:
Foster.Mike, Foster.Lilly
Ted.Nash, Josh.Michel
Working SQL fiddle
Docs: https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_group-concat
You need CTE with STUFF() :
WITH t AS (
SELECT t1.*, t2.emailname
FROM table1 t1 INNER JOIN
table2 t2
ON t2.emailids = t1.emailids
)
SELECT id, STUFF ( (SELECT DISTINCT ','+t1.emailname
FROM t t1
WHERE t1.id = t.id
FOR XML PATH ('')
), 1, 1, ''
) AS Email_Name
FROM t
GROUP BY id;
You were actually close but your SQL doesn't match your schema. This one works as you want:
SELECT Email_Name =
STUFF((SELECT DISTINCT ', ' + EmailIDs
FROM table1 b
WHERE b.Component_ID= a.Component_ID
and [Role] ='Team Lead' and Functional_Group ='Product'
FOR XML PATH('')), 1, 2, '')
FROM table1 a
GROUP BY a.Component_ID;
EDIT: I didn't understand what you are asking exactly. Might this be what you meant?
SELECT STUFF((SELECT ', ' + EmailName
FROM Table2 where EmailIDs IN ( SELECT EmailIDs
FROM table1
WHERE [Role] ='Team Lead' and Functional_Group ='Product')
FOR XML PATH('')), 1, 2, '')
Or you meant this:
SELECT DISTINCT Component_ID, emailNames
FROM table1
CROSS APPLY (SELECT STUFF((SELECT ', '+t2.EmailName
FROM table2 t2
INNER JOIN TABLE1 t1 ON t1.EmailIDs = t2.EmailIDs
WHERE t1.Component_ID = Table1.Component_ID
FOR XML PATH('')), 1, 2, '')
) t(EmailNames)
WHERE [Role] ='Team Lead' and Functional_Group ='Product'
Here is SQLFiddle Demo

how to omit null values using SQL query

I am trying to only display the rows in which there is date for Researchers.
I cannot manage to omit the rows with Null Values. I even tried this solution How to remove null rows from sql query result?..
This is my Query:
SELECT Submission.Title AS [Submission_Title], CA.Surname AS [Researchers], Submission.Status AS [Status]
FROM Submission
CROSS APPLY (SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE CONCAT (DATENAME(MONTH,[Submission].[SubmissionDate]), ' ',DATEPART (YEAR,[Submission].[SubmissionDate])) = 'October 2015'
AND Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')) AS CA (Surname)
GROUP BY convert(varchar(10),datename(month,Submission.SubmissionDate)), Submission.Title, CA.Surname, Submission.Status;
This is my Current output:
any suggestion. Thank you
Quickfix, without reading query:
WITH cte AS
(
SELECT Submission.Title AS [Submission_Title], CA.Surname AS [Researchers], Submission.Status AS [Status]
FROM Submission
CROSS APPLY (SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE CONCAT (DATENAME(MONTH,[Submission].[SubmissionDate]), ' ',DATEPART (YEAR,[Submission].[SubmissionDate])) = 'October 2015'
AND Submission.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ')) AS CA (Surname)
GROUP BY convert(varchar(10),datename(month,Submission.SubmissionDate)), Submission.Title, CA.Surname, Submission.Status
)
SELECT *
FROM cte
WHERE Researchers IS NOT NULL;
There is probably more elegant solution, but you need to share sample data and structures.
This part may cause problems:
SELECT DISTINCT ', ' + r.Surname
try with CONCAT instead or :
SELECT DISTINCT ', ' + ISNULL(r.Surname, '')
You should filter out the researchers before the group by rather than afterwards. When possible, it is better (performance-wise) to put conditions before aggregation.
SELECT s.Title AS Submission_Title, CA.Surname AS Researchers, s.Status
FROM Submission s CROSS APPLY
(SELECT STUFF((SELECT DISTINCT ', ' + r.Surname
FROM ResearcherSubmission rs INNER JOIN
Researcher r
ON r.ResearcherID = rs.ResearcherID
WHERE s.SubmissionID = rs.SubmissionID
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'
), 1, 2, ' '))
) AS CA(Surname)
WHERE s.SubmissionDate >= '2015-10-01' AND s.SubmissionDate < '2015-11-01' AND
ca.Surname IS NULL
GROUP BY YEAR(s.SubmissionDate), MONTH(s.SubmissionDate), s.Title, CA.Surname, s.Status;
Note the changes made:
Table aliases make the query easier to write and to read.
I changed the date comparison to have no functions on the date itself. This would allow SQL Server to use an index, if appropriate.
I also moved the date comparison from the CROSS APPLY subquery to the outer query. This could be a big gain in efficiency. Why do the extra work for rows that will be filtered out anyway?
I added the NOT NULL condition to the WHERE clause.
The date key in the outer GROUP BY is redundant because the query is only using one month of data. I simplified the logic but left it.

How to concatenate distinct columns of otherwise duplicate rows into one row without using FOR XML PATH('')?

I have this query:
SELECT DISTINCT
f1.CourseEventKey,
STUFF
(
(
SELECT '; ' + Title
FROM (
SELECT DISTINCT
ces.CourseEventKey,
f.Title
FROM CourseEventSchedule ces
INNER JOIN Facility f ON f.FacilityKey = ces.FacilityKey
WHERE ces.CourseEventKey IN
(
SELECT CourseEventKey
FROM #CourseEvents
)
) f2
WHERE f2.CourseEventKey = f1.CourseEventKey
FOR XML PATH('')
), 1, 2, ''
)
FROM (
SELECT DISTINCT
ces.CourseEventKey,
f.Title
FROM CourseEventSchedule ces
INNER JOIN Facility f ON f.FacilityKey = ces.FacilityKey
WHERE ces.CourseEventKey IN
(
SELECT CourseEventKey
FROM #CourseEvents
)
) f1
It produces this result set:
CourseEventKey Titles
-------------- ----------------------------------
29 Test Facility 1
30 Memphis Training Room
32 Drury Inn & Suites Creve Coeur
The data is accurate, but I can't have FOR XML PATH('') because it escapes certain special characters.
To be clear, I'm using FOR XML PATH('') because it is possible for records with the same CourseEventKey to have multiple Facility titles associated with them.
How can I retain the data returned by this query without using FOR XML PATH('')?
Change the "for xml path('') )" section to "for xml path(''), root('root'), type).query('root').value ('.', 'varchar(max)')" this will unescape the characters correctly.
Sorry for the poor formatting but not at my computer right now. I can give a full example later if you need.