SQL Server - Concatenate - Stuff doesn't work - sql

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;

Related

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 get values other from a string in SQL

I'm using the stuff function to get a list of ids. But there are some values that are duplicate:
----------
3180,3181,3182,3180,4180
----------
3183,3184,3184,4181
I just want to get the list of ids that are not duplicated, desired results:
----------
3181,3182,4180
----------
3183,4181
This my query:
SELECT oa.AssetId,
oal.AssetLineId,
SNIds = STUFF((SELECT ', ' + CAST(isn1.Id AS VARCHAR(MAX))
FROM dbo.InventoryOperation o1
INNER JOIN dbo.InventoryOperationAsset oa1 ON oa1.OperationId = o1.OperationId
INNER JOIN dbo.InventoryOperationAssetLine oal1 ON oal1.OperationAssetId = oa1.OperationAssetId
INNER JOIN dbo.InventoryOperationAssetLineSerialNumber ioalsn1 ON ioalsn1.OperationAssetLineId = oal1.OperationAssetLineId
INNER JOIN dbo.InventorySerialNumber isn1 ON isn1.Id = ioalsn1.SerialId
WHERE oa.AssetId = oa1.AssetId AND oal.AssetLineId = oal1.AssetLineId
FOR XML PATH('')),1,2,'')
FROM dbo.InventoryOperation o
INNER JOIN dbo.InventoryOperationAsset oa ON oa.OperationId = o.OperationId
INNER JOIN dbo.InventoryOperationAssetLine oal ON oal.OperationAssetId = oa.OperationAssetId
INNER JOIN dbo.InventoryOperationAssetLineSerialNumber ioalsn ON ioalsn.OperationAssetLineId = oal.OperationAssetLineId
GROUP BY oa.AssetId, oal.AssetLineId;
Can anyone help me with this issue? Thanks in advance if anyone could help.
You could use
SELECT DISTINCT columns FROM table;
That makes sure you only get non duplicates.
Query that provides data to STUFF function should use following query to get only unique ids (e.g. 3181,3182,4180,3183,4181) and not the ones that have duplicates (e.g. 3180,3184)
SELECT column_name
FROM TABLE_NAME
GROUP BY column_name
HAVING COUNT(*) = 1;
Updating answer with STUFF function sample ...
SELECT STUFF(', ' + REPLACE((SELECT id + ', ' AS 'data()'
FROM stackoverflow
GROUP BY id
HAVING COUNT(*) = 1
FOR XML PATH('')), '', ''), 1, 2, '')
Sample Run

display more than one value using a SQL query

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

How to Retrieve Column Headers of a Select Query?

How it is possible to retrieve column headers of a select query as a single column in SQL Server ? (it is preferred to retrieve data type of columns )
Query example:
select a.PartId, a.PartName, b.GroupName
from Parts as a
inner join Groups as b on a.GroupRef = b.GroupId
Expected result:
Columns
--------
PartId
PartName
GroupName
Starting from SQL Server 2012+ you can use sys.dm_exec_describe_first_result_set to get all metadata about result set:
DBFiddle Demo
DECLARE #tsql NVARCHAR(MAX) =
N'select a.PartId , a.PartName , b.GroupName
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId';
SELECT name AS [Columns]
FROM sys.dm_exec_describe_first_result_set(#tsql, NULL, 1)
One way is to create a temporary table with the schema of resultset and then query tempdb's schema table to get the column names and details. You can get all needed details.
select a.PartId , a.PartName , b.GroupName into #yourtable
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId
where 1=2
SELECT c.name as columnname,t.name as datatype
FROM tempdb.sys.columns c
inner join tempdb.sys.systypes as t on t.xtype = c.system_type_id
WHERE [object_id] = OBJECT_ID(N'tempdb..#yourtable');
SELECT 'PartId', 'PartName', 'GroupName'
UNION ALL
select a.PartId , a.PartName , b.GroupName
from Parts as a inner join Groups as b
on a.GroupRef = b.GroupId

trying to concatenate a column into a comma delimited list

i have 3 tables, 1 for products and one for categories the products are assigned to. what IM trying to do is concatenate the column called stCategoryName to a single column in a comma delimited list.
Basically I have the products table containing the primary key for each product and im trying to figure out how to concatenate all the stcategoryName column next to each product so i can have a simplified return
what im trying to get is the following.
stProductID stCategoryName
123 category1,category2,category3
SELECT
dbo.StoreItemTracking.StCategoryID,
dbo.StoreItemTracking.StProductID,
dbo.StoreItemTracking.viewOrder,
dbo.StoreCategories.StCategoryName,
dbo.Store_Products.PartNumber
FROM
dbo.StoreItemTracking
INNER JOIN dbo.StoreCategories
ON dbo.StoreItemTracking.StCategoryID = dbo.StoreCategories.StCategoryID
INNER JOIN dbo.Store_Products
ON dbo.StoreItemTracking.StProductID = dbo.Store_Products.ID
Im stuck as to how to concatenate a column where the query contains 3 tables to select from.
any help greatly appreciated
Look at using coalesce to turn category into a CSV:
See example:
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '')
+ CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
You can also use CTE's or Subqueries. See:
http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=createacommadelimitedlist
Another nice and easy example:
http://www.codeproject.com/Articles/21082/Concatenate-Field-Values-in-One-String-Using-CTE-i
This:
FId FName
--- ----
2 A
4 B
5 C
6 D
8 E
with:
;WITH ABC (FId, FName) AS
(
SELECT 1, CAST('' AS VARCHAR(8000))
UNION ALL
SELECT B.FId + 1, B.FName + A.FName + ', '
FROM (And the above query will return
SELECT Row_Number() OVER (ORDER BY FId) AS RN, FName FROM tblTest) A
INNER JOIN ABC B ON A.RN = B.FId
)
SELECT TOP 1 FName FROM ABC ORDER BY FId DESC
becomes:
FName
----------------------------
A, B, C, D, E,
Don't understand how your products and categories are connected but in general I do like this to create comma separated lists.
SELECT table1.Id
,Csv
FROM table1
CROSS APPLY (
-- Double select so we can have an alias for the csv column
SELECT (SELECT ',' + table2.Name
FROM table2
WHERE table2.Id = table1.Id
FOR XML PATH('')
) AS RawCsv
) AS CA1
CROSS APPLY (
-- Trim the first comma
SELECT RIGHT(RawCsv, LEN(RawCsv) - 1) AS Csv
) AS CA2