String_agg equivalent function in SQL server [duplicate] - sql

This question already has answers here:
String_agg for SQL Server before 2017
(2 answers)
Closed 2 years ago.
I want to rewrite my SQL query with stuff or other option available instead of string_agg function as my SQL server doesn't support it.
Can anyone please help me with this?
select String_agg(air.code,',') AS Code,String_agg(air.Id,',') AS AId,res.ResId
from Table1 air
inner join
Table2 rmap on air.Id=airmap.Id
inner join Table3 res on rmap.ResId=res.ResId
group by res.ResId
with output like below

Presumably, you want something like this:
select res.ResId,
stuff( (select ',' + air.code
from table1 air inner join
Table2 rmap
on air.Id = airmap.Id
where rmap.ResId = res.ResId
for xml path ('')
), 1, 1, '') as codes,
stuff( (select ',' + air.aid
from table1 air inner join
Table2 rmap
on air.Id = airmap.Id
where rmap.ResId = res.ResId
for xml path ('')
), 1, 1, '') as aids
from Table3 res ;

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;

Long running query sql

I have a SQL query to load from a view into a table, it is running for more than 45 minutes now. I have checked if there is any blocking issue but this is the only query running and I am not using any index in a destination table. It only takes 5 minutes to create the underlying view. It has 5 million records.
INSERT INTO [dbo].[Table1]
SELECT
Name,
Service = STUFF ((SELECT ',' + Service
FROM Table2 T1
WHERE T1.Resource = T2.Resource
ORDER BY Service
FOR XML PATH('')), 1, 1, ''),
Tag = STUFF ((SELECT ',' + Tag
FROM Table2 T1
WHERE T1.Resource = T2.Resource
ORDER BY Tag
FOR XML PATH('')), 1, 1, ''),
Resource,
MAX(SubjectIdentifier) AS SubjectIdentifier,
DataType
FROM
Table2 T2 WITH (NOLOCK)
GROUP BY
Name, Resource, DataType
How can I optimize this query and load it fast? Thanks
Let me assume that you cannot use string_agg() because you are using an old version of SQL Server.
I would suggest rewriting your query a little bit and then adding indexes:
SELECT Name,
Service = STUFF((SELECT ',' + TT2.Service
FROM Table2 TT2
WHERE TT2.Resource = T2.Resource
ORDER BY TT2.Service
FOR XML PATH('')
), 1, 1, ''
),
Tag = STUFF((SELECT ',' + TT2.Tag
FROM Table2 TT2
WHERE TT2.Resource = T2.Resource
ORDER BY TT2.Tag
FOR XML PATH('')
), 1, 1, ''
),
Resource,
MAX(SubjectIdentifier) AS SubjectIdentifier,
DataType
FROM (SELECT DISTINCT Name, Resource, DataType
FROM Table2 T2
) T2;
Then, you want indexes on:
Table2(Name, Resource, DataType)
Table2(Resource, Service)
Table2(Resource, Tax)
In addition, it is suspicious that you are aggregating by three columns but only using one of them in the subqueries. I would expect all three in the subqueries.
Also notice that I qualified all column references.

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

Grouping the results of multiple rows in one column [duplicate]

This question already has answers here:
Can I Comma Delimit Multiple Rows Into One Column? [duplicate]
(5 answers)
Closed 10 years ago.
The query below gives me two results where everything is same except the last column v2_roles.id. Is there a way to group them in one column (may be a comma seprated list) and get just one result back
SELECT v2_admin.adminid,
v2_admin.adminname,
v2_admin.login,
v2_admin.email,
v2_roles.id
FROM v2_admin
INNER JOIN v2_admin_roles
ON v2_admin.adminid = v2_admin_roles.adminid
INNER JOIN v2_roles
ON v2_admin_roles.roleid = v2_roles.id
WHERE v2_admin.adminid = 2
This should also work,
;WITH mytable AS (
SELECT v2_admin.adminid,
v2_admin.adminname,
v2_admin.login,
v2_admin.email,
v2_roles.id
FROM v2_admin
INNER JOIN v2_admin_roles
ON v2_admin.adminid = v2_admin_roles.adminid
INNER JOIN v2_roles
ON v2_admin_roles.roleid = v2_roles.id
WHERE v2_admin.adminid = 2
)
SELECT t1.adminid,t1.adminname, t1.login, t1.email,
roleIds =REPLACE( (SELECT convert(varchar,id) AS [data()]
FROM mytable t2
WHERE t2.adminid = t1.adminid
--ORDER BY t2.adminid
FOR XML PATH('')
), ' ', ' , ')
FROM mytable t1
GROUP BY t1.adminid,t1.adminname, t1.login, t1.email ;
This is a little trick using FOR XML to convert the field to a list and STUFF to remove the beginning comma -- this should be close (untested):
SELECT v2_admin.adminid,
v2_admin.adminname,
v2_admin.login,
v2_admin.email,
STUFF(
(
SELECT ',' + CAST(v2_roles.id as varchar) AS [text()]
FROM v2_roles
WHERE v2_admin_roles.roleid = v2_roles.id
ORDER BY v2_roles.id
FOR XML PATH('')
), 1, 1, '') AS Roles
FROM v2_admin
INNER JOIN v2_admin_roles
ON v2_admin.adminid = v2_admin_roles.adminid
WHERE v2_admin.adminid = 2
GROUP BY v2_admin.adminid,
v2_admin.adminname,
v2_admin.login,
v2_admin.email