Concatenating Column Values into a Comma-Separated List - sql-server

What is the TSQL syntax to format my output so that the column values appear as a string, seperated by commas.
Example, my table CARS has the following:
CarID CarName
----------------
1 Porsche
2 Mercedes
3 Ferrari
How do I get the car names as : Porsche, Mercedes, Ferrari

SELECT LEFT(Car, LEN(Car) - 1)
FROM (
SELECT Car + ', '
FROM Cars
FOR XML PATH ('')
) c (Car)

You can do a shortcut using coalesce to concatenate a series of strings from a record in a table, for example.
declare #aa varchar (200)
set #aa = ''
select #aa =
case when #aa = ''
then CarName
else #aa + coalesce(',' + CarName, '')
end
from Cars
print #aa

If you are running on SQL Server 2017 or Azure SQL Database you do something like this :
SELECT STRING_AGG(CarName,',') as CarNames
FROM CARS

You can do this using stuff:
SELECT Stuff(
(
SELECT ', ' + CARS.CarName
FROM CARS
FOR XML PATH('')
), 1, 2, '') AS CarNames

DECLARE #CarList nvarchar(max);
SET #CarList = N'';
SELECT #CarList+=CarName+N','
FROM dbo.CARS;
SELECT LEFT(#CarList,LEN(#CarList)-1);
Thanks are due to whoever on SO showed me the use of accumulating data during a query.

Another solution within a query :
select
Id,
STUFF(
(select (', "' + od.ProductName + '"')
from OrderDetails od (nolock)
where od.Order_Id = o.Id
order by od.ProductName
FOR XML PATH('')), 1, 2, ''
) ProductNames
from Orders o (nolock)
where o.Customer_Id = 525188
order by o.Id desc
(EDIT: thanks #user007 for the STUFF declaration)

Please try this with the following code:
DECLARE #listStr VARCHAR(MAX)
SELECT #listStr = COALESCE(#listStr+',' , '') + CarName
FROM Cars
SELECT #listStr

DECLARE #SQL AS VARCHAR(8000)
SELECT #SQL = ISNULL(#SQL+',','') + ColumnName FROM TableName
SELECT #SQL

Related

Port STRING_AGG to FOR XML

I'm currently working on Data Discovery and Classification and I have a query that allows me to see a preview of the data while doing the classification.
This is an example of how it works against AdventureWorks:
DECLARE #TableName VARCHAR(100) = 'Product'
DROP TABLE IF EXISTS #ColumnsToDisplay
SELECT ROW_NUMBER () OVER (ORDER BY tab.name) AS Iteration,
SCHEMA_NAME (tab.schema_id) AS schema_name,
tab.name AS table_name,
--col.column_id,
col.name AS column_name,
--t.name AS data_type,
--col.max_length,
--col.precision,
CAST(NULL AS VARCHAR(MAX)) AS DataSample
INTO #ColumnsToDisplay
FROM sys.tables AS tab
JOIN sys.columns AS col
ON col.object_id = tab.object_id
--LEFT JOIN sys.types AS t
-- ON col.user_type_id = t.user_type_id
WHERE tab.name = #TableName
DECLARE #Iterations INT = 0,
#CurrentIteration INT = 1;
SELECT #Iterations = MAX (Iteration)
FROM #ColumnsToDisplay
WHILE #CurrentIteration <= #Iterations
BEGIN
DECLARE #CurrentTableName VARCHAR(100) = '',
#CurrentColumnName VARCHAR(100) = '',
#DynamicQuery NVARCHAR(1000) = N''
DECLARE #Sample VARCHAR(MAX)
SET #CurrentTableName = '';
SET #DynamicQuery = N'';
SELECT #CurrentTableName = CONCAT (ttq.schema_name, '.', ttq.table_name),
#CurrentColumnName = ttq.column_name
FROM #ColumnsToDisplay AS ttq
WHERE ttq.Iteration = #CurrentIteration
IF (#CurrentTableName = '')
BEGIN
SET #CurrentIteration += 1
CONTINUE
END
SET #DynamicQuery = CONCAT (N'
SELECT #Sample = STRING_AGG(t.ColumnData,'', '')
FROM (
SELECT TOP 5 CAST(x.', #CurrentColumnName, ' AS VARCHAR(MAX)) AS ColumnData
FROM ', #CurrentTableName, ' AS x
WHERE x.', #CurrentColumnName, ' IS NOT NULL
)t')
EXECUTE sys.sp_executesql #DynamicQuery,
N'#Sample VARCHAR(MAX) OUTPUT',
#Sample = #Sample OUTPUT
UPDATE #ColumnsToDisplay
SET DataSample = #Sample
WHERE Iteration = #CurrentIteration
SET #CurrentIteration += 1
END
SELECT ctd.Iteration,
ctd.schema_name,
ctd.table_name,
--ctd.column_id,
ctd.column_name,
--ctd.data_type,
--ctd.max_length,
--ctd.precision,
ctd.DataSample
FROM #ColumnsToDisplay AS ctd
Here the result:
Iteration
schema_name
table_name
column_name
DataSample
1
Production
Product
ProductID
980, 365, 771, 404, 977
2
Production
Product
Name
Adjustable Race, All-Purpose Bike Stand, AWC Logo Cap, BB Ball Bearing, Bearing Ball
3
Production
Product
ProductNumber
AR-5381, BA-8327, BB-7421, BB-8107, BB-9108
4
Production
Product
MakeFlag
0, 0, 1, 0, 1
5
Production
Product
FinishedGoodsFlag
0, 0, 0, 0, 0
6
Production
Product
Color
Black, Black, Black, Silver, Silver
The problem is that this query only works from SQL Server 2017 and above because it uses STRING_AGG. For SQL Server 2016 and below I'm supposed to use STUFF instead.
I followed this example but I couldn't really fix it.
The only thing I know is that the part of the code that I need to port is this:
SET #DynamicQuery = CONCAT (N'
SELECT #Sample = STRING_AGG(t.ColumnData,'', '')
FROM (
SELECT TOP 5 CAST(x.', #CurrentColumnName, ' AS VARCHAR(MAX)) AS ColumnData
FROM ', #CurrentTableName, ' AS x
WHERE x.', #CurrentColumnName, ' IS NOT NULL
)t')
Can anyone help me port STRING_AGG to STUFF?
Thank you
First, let's clarify that stuff is, as Stu mentions, just used to remove the first delimiter.
So, if you had
'a','b','c'
and you wanted to use a commas delimiter, xml path would give you:
,a,b,c
and you use stuff to cut off the first comma:
a,b,c
Your query seems to have no delimiters at all, so you won't use stuff. With that out of the picture, here's me untested guess, it's tricky what with the dynamic query added on top of it:
SET #DynamicQuery = CONCAT (N'
set #Sample =
(
select t.ColumnData
from ', #CurrentTableName, ' AS x
where x.', #CurrentColumnName, ' IS NOT NULL
for xml path(''''), type
).value(''.'',''nvarchar(max)'')
)t')
Than you for your help.
The right question was:
SET #DynamicQuery = CONCAT (N'
SELECT #Sample = STUFF((SELECT '', ''+ t.ColumnData
FROM (
SELECT TOP 5 CAST(x.[', #CurrentColumnName, '] AS VARCHAR(MAX)) AS ColumnData
FROM ', #CurrentTableName, ' AS x
WHERE x.[', #CurrentColumnName, '] IS NOT NULL
) AS t
FOR XML PATH('''')),1,1,'''')')

Prepare SUM function for the given dynamic string

Given string:
Note: The following comma separted string is dynamic which comes with any combination.
DECLARE #Str varchar(max) = '[A-B],[B-C],[C-D],[D-E]'
Expected Result:
SUM([A-B]) AS [A-B],SUM([B-C]) AS [B-C],SUM([C-D]) AS [C-D],SUM([D-E]) AS [D-E]
My try:
SELECT 'SUM('+REPLACE(#Str,',','),SUM(')+')'
Output:
SUM([A-B]),SUM([B-C]),SUM([C-D]),SUM([D-E])
Try this
DECLARE #Str AS TABLE ([Str] varchar(max) )
INSERT INTO #Str
SELECT '[A-B],[B-C],[C-D],[D-E]'
;WITH CTE
AS
(
SELECT 'SUM( '+Split.a.value('.','nvarchar(100)')+' ) AS 'AS [Str],Split.a.value('.','nvarchar(100)') AS [Str1]
FROM
(
SELECT CAST('<S>'+(REPLACE([Str],',','</S><S>')+'</S>') AS XML ) AS [Str]
FROM #Str
)AS A
CROSS APPLY [Str].nodes('S') AS Split(a)
)
SELECT STUFF((SELECT DISTINCT ', '+ CONCAT([Str], [Str1])
FROM CTE
FOR XML PATH ('')),1,1,'') AS ExpectedResult
Result
ExpectedResult
-------------------
SUM( [A-B] ) AS [A-B], SUM( [B-C] ) AS [B-C], SUM( [C-D] ) AS [C-D], SUM( [D-E] ) AS [D-E]
Being SQL Server 2008 you would first need to create an SplitString function (it's already included in SQL Server 2016 and forward), like this one :
T-SQL split string
Then you can calculate your clauses :
select 'sum(' + Name + ', as ' + Name
from SplitString(#Str)
And finally you only need to concatenate all those rows, for example adding for xml path('')
select 'sum(' + Name + ', as ' + Name + ','
from SplitString(#Str)
for xml path('')
Simple way to achieve your task
Declare #str varchar(max) = '[A-B],[B-C],[C-D],[D-E]'
, #Main varchar(max)=''
select #Main += ',sum('+a+')'
from (select distinct value as a from STRING_SPLIT(#str , ',')) as Splt
set #Main= stuff(#Main ,1,1,'')
print #Main
Result : sum([A-B]),sum([B-C]),sum([C-D]),sum([D-E])

SQL Server Selecting DISTINCT record to string variable

I want to select all distinct name from person table to VARCHAR variable .
I have write a query that returns all name as follow.
DECLARE #pName as VARCHAR(MAX)
SET #pName=''
SELECT #pName += RTRIM(FullName) + ','
FROM Persons
SELECT #pName
When I try to select distinct on FullName, SQL Server throws an exception.
DECLARE #pName as VARCHAR(MAX)
SET #pName=''
SELECT DISTINCT FullName, #pName += RTRIM(FullName) + ','
FROM Persons
SELECT #pName
Msg 141, Level 15, State 1, Line 3
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
Distinct on variable name return only first Name
SELECT DISTINCT #pName += RTRIM(FullName) +
FROM Persons
SELECT #pName
How can I select distinct name to string variable from SELECT statement?
Thanks in advance
You can get the same distinct result without using the variable also.
SELECT STUFF((SELECT distinct ',' + FullName FROM Persons
FOR xml path ('')
), 1, 1, '')
Using variable - Add a group By
DECLARE #pName as VARCHAR(MAX)
set #pName=''
SELECT #pName+= RTRIM(FullName) + ',' FROM Persons Group By FullName
select #pName
DECLARE #pName as VARCHAR(MAX)
with cte as(
SELECT DISTINCT FullName FROM Persons
)
Select #pName = coalesce(#pName + ', ', '') + FullName
from cte
select #pName

What is the T-SQL syntax to exclude a duplicate column in the output when joining 2 tables?

I am using SQL Server 2014 and I have the following T-SQL query which joins 2 tables:
SELECT a.*, b.* FROM TEMP a
INNER JOIN Extras b ON b.ResaID = a.ResaID
I would like to pull ALL the columns from TEMP and all the columns from "Extras" with the exception of the ResaID column as it is already included in a.* in the above query. Basically, I want to pull a.* + b.* (excluding b.ResaID).
I know I can write the query in the form:
Select a.*, b.column2, b.column3,...
but since b.* has got around 40 columns, is there a way to write the query in a more simplified way to exclude b.ResaID, rather than specify each of the columns in the "Extras" table?
Unfortunately, there is no such syntax. You could either use asterisks (*) and just ignore the duplicated column in your code, or explicitly list the columns you need.
You should create a view and select the columns you need from that view. Here is a script that will generate that view for you:
DECLARE #table1 nvarchar(20) = 'temp'
DECLARE #table1key nvarchar(20) = 'ResaID'
DECLARE #table2 nvarchar(20) = 'Extras'
DECLARE #table2key nvarchar(20) = 'ResaID'
DECLARE #viewname varchar(20) = 'v_myview'
DECLARE #sql varchar(max) = ''
SELECT #sql += '], a.[' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
SELECT #sql += '], b.[' + column_name
FROM
(
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
EXCEPT
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
) x
SELECT
#sql = 'CREATE view ' +#viewname+ ' as SELECT '
+ STUFF(#sql, 1, 3, '') + '] FROM ['
+#table1+ '] a JOIN ['+ #table2
+'] b ON ' + 'a.' + #table1key + '=b.' + #table2key
EXEC(#sql)
You can simply solve this using a dynamic sql query.
DECLARE #V_SQL AS NVARCHAR(2000)='' --variable to store dynamic query
,#V_TAB1 AS NVARCHAR(200)='TEMP' --First Table
,#V_TAB2 AS NVARCHAR(200)='Extras' --Second Table
,#V_CONDITION AS NVARCHAR(2000)='A.ResaID = B.ResaID' --Conditions
SELECT #V_SQL = STUFF(
( SELECT ', '+TCOL_NAME
FROM
( SELECT 'A.'+S.NAME AS TCOL_NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1
UNION ALL
SELECT 'B.'+S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB2
AND S.NAME NOT IN (SELECT S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1)
) D
FOR XML PATH('')
),1,2,'')
EXECUTE ('SELECT '+#V_SQL+'
FROM '+#V_TAB1+' AS A
INNER JOIN '+#V_TAB2+' AS B ON '+#V_CONDITION+' ')

TSQL How to build a string dynamically?

let's say that I have a table with columns like this:
| Name | Cat_id |
I cannot remember the function which can build a varchar like this
1,24|4,56|5,67
where the number before the comma is the Cat_id, and the number after comma is the count of rows with that ID. Any ideas ?
This could do the trick:
declare #s varchar(8000)
set #s = ''
select #s = #s + cast(cat_id as varchar(20)) + ',' + cast(count(*) as varchar(20)) + '|'
from SomeTable
group by cat_id
option(maxdop 1) -- this sure there are no funny side-effects because of parallelism
print #s
Alternatively you could use 'for xml', or a cursor, but this should be faster.
Regards GJ
Hope this will help(sql server 2005+)... I have not tested the program as I donot have SQL Server at present
With Cte1 As(
Select Cat_ID, Count(Cat_ID),Cast(Cat_ID as Varchar) + Cast(Count(Cat_ID)) as MergedColumn from tbl
group by Cat_ID),
cte2 as(
Select
Cat_ID
, mergedData = stuff((Select cast(MergedColumn + '|' as varchar) from tbl t2 where t2.Cat_ID = t1.Cat_ID
for xml path('')),1,1,'')
from tbl t1)
select mergedData from Cte2

Resources