SQL Server Convert Particular Column Values into comma separated String - sql

I have a table in Database as below :
Id Name
1 Item1
1 Item2
2 Item3
1 Item4
3 Item5
I need output as below(3rd column is count):
1 Item1,Item2,Item4 3
2 Item3 1
3 Item5 1
How it can achieved by SQL Query ?

SQL Server has STUFF() function which could able to help you.
SELECT t.Id,
Name = STUFF( (SELECT DISTINCT ','+Name
FROM table
WHERE Id = t.Id
FOR XML PATH('')
), 1, 1, ''
)
FROM table t
GROUP BY t.Id;

SQL Server 2017 has introduced a much easier way to achieve this using STRING_AGG(expression, separator).
Here's an example:
SELECT STRING_AGG(T.Name, ', ') FROM MyTable T where MyColumnID = 78
You could even play around with formatting in other ways like this one:
SELECT STRING_AGG(CONCAT(T.MyColumnID,' - ',T.Name), ', ') FROM MyTable T where MyColumnID = 78
More info in this blog I found about it: https://database.guide/the-sql-server-equivalent-to-group_concat/

select id, group_concat(name) csv,
from Table
group by id

Related

SQL Concatenate and group

I need a TSQL version of group_concat
Simmilar to the example found here:
Policy Destination ID
-------------------------
PolA DestA 1
PolA DestA 2
PolB DestB 3
PolB DestB 4
PolC DestC 5
PolC DestC 6
PolC DestD 7
The output should look like this:
PolA DestA 1,2
PolB DestB 3,4
PolC DestC 5,6
PolC DestD 7
The grouping is on the uniqueness of both the first 2 columns, and then a concatenated output on the third.
I found this link but it only take into account 2 columns
Any help would be appreciated.
You can try this :
SELECT G.Policy, G.Destination,
stuff(
(select cast(',' as varchar(max)) + U.ID
from yourtable U
WHERE U.Policy = G.Policy and U.Destination = G.Destination
order by U.Policy
for xml path('')
), 1, 1, '') AS IDs
FROM yourtable G group BY G.Policy, G.Destination
In MSSQL Synax:
SELECT Policy, Destination, STRING_AGG ( [ID], ',' ) IDs
FROM Table
I just create the PolA example table for you, just replace the CTE as your table, try below:
WITH ABC
as
(
select 'PolA' as Policy,'DestA' as Destination,'1' as ID
UNION ALL
select 'PolA','DestA','2'
)
SELECT Policy, Destination,
STUFF((SELECT ',' + A.ID FROM ABC as A WHERE A.Policy = B.Policy FOR XML PATH('')),1,1,'') as ID
FROM ABC as B
GROUP BY B.policy, B.Destination

SQL: Pivoting on more than one column

I have a table
Name | Period | Value1 | Value2
-----+---------+---------+-------
A 1 2 3
A 2 5 4
A 3 6 7
B 1 2 3
B 2 5 4
B 3 6 7
I need results like
Name | Value1 | Value2
-----+--------+------
A | 2,5,6 | 3,4,7
B | 2,5,6 | 3,4,7
Number of periods is dynamic but I know how to handle it so, for simplicity, let's say there are 3 periods
The query below gives me results for Value1. How can I get results for both?
I can always do them separately and then do a join but the table is really big and I need "combine" four values, not two. Can I do it in one statement?
SELECT Name,
[1]+','+ [2] + ','+ [3] ValueString
FROM (
select Name, period, cpr from #MyTable
) as s
PIVOT(SUM(Value1)
FOR period IN ([1],[2],[3])
Use conditional aggregation. Combining the values into strings is a bit tricky, requiring XML logic in SQL Server:
select n.name,
stuff((select ',' + cast(value1 as varchar(max))
from t
where n.name = t.name
order by t.period
for xml path ('')
), 1, 1, ''
) as values1,
stuff((select ',' + cast(value2 as varchar(max))
from t
where n.name = t.name
order by t.period
for xml path ('')
), 1, 1, ''
) as values2
from (select distinct name
from t
) n;
Your values look like numbers, hence the explicit cast and the lack of concern for XML special characters.
You may ask why this does the distinct in a subquery rather than in the outer query. If done in the outer query, then the SQL engine will probably do the aggregation for every row before doing the distinct. I'm not sure if the optimizer is good enough run the subqueries only once per name.
Using Group By with stuff function and get expected result
SELECT Name , STUFF((SELECT ',' + CAST(Value1 AS VARCHAR) FROM #MyTable T2 WHERE T1.Name = T2.Name FOR XML PATH('')),1,1,'') Value1
, STUFF((SELECT ',' + CAST(Value2 AS VARCHAR) FROM #MyTable T3 WHERE T1.Name = T3.Name FOR XML PATH('')),1,1,'') Value2 FROM #MyTable T1 GROUP BY Name

Can you use SELECT * and set condition across all columns

I have a data table with about 30 columns 1 column is a user id and the rest of them are "items" with the values of ranking from 0 - 29 which indicates an the amount of interest in the item. 0 = No interest 1 = Most and 29 = Least.
Is there away to output all columns with a count without having to write a statement or for each item leveraging the "*" such as
SELECT COUNT(*)
FROM Table t
WHERE * != 0
The output would be
Item1 | Item2 | Item3 | Item4 | Item5
xxxxx | xxxxx | xxxxx | xxxxx | xxxxx
xxxxx being the total count of records within the column where value is not 0?
You could go through the INFORMATION_SCHEMA and build a query and run it with the sql function EXEC().
Join together INFORMATION_SCHEMA.TABLES with INFROMATION_SCHEMA.COLUMNS. Also just encase you want to reuse it place a variable with the wanted table name. You'll have to filter INFORMATION_SCHEMA.COLUMNS by the type column you need (int)
There is an XML trick to dynamically normalize data without explicit columns enumeration:
With SampleTab (UserId, Item1, Item2) As (
Select 'A' , 0 , 1 Union All
Select 'B' , 2 , 0 Union All
Select 'C' , 3 , 0
), SampleXML As (
Select CAST((Select * From SampleTab root For XML Auto) AS XML) As Doc
), SampleNormalised As (
Select
NodeName = C.value('local-name(.)', 'varchar(50)'),
NodeValue = C.value('(.)[1]', 'int')
From SampleXML
Cross Apply Doc.nodes('/root/#*') AS T(C)
)
Select NodeName As Item, Count(*) As CountNot0
From SampleNormalised
Where NodeName <> 'UserId' AND NodeValue <> 0
Group By NodeName;
Result:
Item CountNot0
----- ---------
Item1 2
Item2 1
Not sure that will help you, but there is no way in SQL to skip one column (User Id) in the result set without enumerating all the other columns apart from Dynamic SQL mentioned by Monofuse anyway.

SQL Server: split a single row into 2 rows

I'm trying to import data from an excel spreadsheet into a SQL Server 2008 database and I need to massage the data a bit first.
I've used the Data Import wizard to load it into a staging table in the database in the following format:
Id ISOCode Data
1 US Foo
2 CA Bar
3 US or CA Blah
In cases where ISO is an OR-delimited string, eg US or CA, I need to split it into 2 rows, so in the final destination table it would look like this:
Id ISOCode Data
1 US Foo
2 CA Bar
3 US Blah
3 CA Blah
I do have a SplitString table-valued function available but I'm not sure how to work it into the equation.
Here is my solution:
SELECT ID,
CASE
WHEN ( ISOCODE LIKE '% or %' ) THEN LEFT(ISOCODE, Charindex('or',
ISOCODE) - 1
)
ELSE ISOCODE
END AS ISOCode,
DATA
FROM TBL
UNION
SELECT ID,
RIGHT(ISOCODE, Len(ISOCODE) - ( Charindex('or', ISOCODE) + 2 ))AS ISOCode
,
DATA
FROM TBL
WHERE ISOCODE LIKE '% or %'
You can take a look at the full solution (with data) on SQL Fiddle.
select t.Id, c.ISOCode, t.Data
from t
cross apply (select charindex(' or ', t.ISOCode) as OrIndex) idx
cross apply
(
select t.ISOCode where idx.OrIndex = 0
union all select left(t.ISOCode, idx.OrIndex - 1) where idx.OrIndex > 0
union all select substring(t.ISOCode, idx.OrIndex + 4, len(t.ISoCode) - idx.OrIndex - 3) where idx.OrIndex > 0
) c
(this query doesn't require 2 table scans)

SQL Query Distinct Column Concatenate Other Column

I have a MS SQL query that joins multiple tables and an example of the results are:
EmailAddress Column2
--------------------- ----------------
sean#abc.com Value1
sean#abc.com Value2
test#abc.com Value5
What I really want to achieve are the following results:
EmailAddress Column2
--------------------- ------------------
sean#abc.com Value1, Value2
test#abc.com Value5
There are other columns that are identical for each row with the same email address. Can anyone help me with the SQL to return distinct email addresses and concatenate the column2 information?
Thanks for any feedback.
Try the answer to this question
In SQL Server 2005+:
WITH q AS
(
SELECT 'sean#abc.com' AS address, 'Value1' AS value
UNION ALL
SELECT 'sean#abc.com', 'Value2'
UNION ALL
SELECT 'test#abc.com', 'Value5'
)
SELECT (
SELECT CASE WHEN ROW_NUMBER() OVER (ORDER BY value) > 1 THEN ', ' ELSE '' END + value
FROM q qi
WHERE qi.address = qo.address
FOR XML PATH('')
)
FROM (
SELECT DISTINCT address
FROM q
) qo
Same answer from Jeremy, but I would use the other answer with XML PATH trick