SQL how to put values on one line - sql

It is necessary that in the second column in a single line should be all related accounts.
This shows an error
Conversion failed when converting the varchar value ',' to data type int.
SELECT [UserID],
STUFF((SELECT ', ' + UserID
FROM #RelatedIDs
WHERE (UserID = t.UserID)
FOR XML PATH('')) ,1,1,'') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID

You could cast the integer value to a varchar:
SELECT [UserID],
STUFF((SELECT ',' + CAST(UserID as VARCHAR(100))
FROM #RelatedIDs
WHERE (UserID = t.UserID)
FOR XML PATH('')) ,1,1,'') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID
If you are running a recent version of SQL Server (2017 or higher), you can get the same result in a much less obsfucated manner with string_agg():
SELECT t.UserID, STRING_AGG(r.UserID, ',') RelIDs
FROM #RelatedIDs t
INNER JOIN #RelatedIDs r on r.UserID = t.UserID
GROUP BY t.UserID
With the query put this way, it is plain to see that it makes little sense. The self-join operates on the same column as the one that defines the group, so this will just generate a list of identical UserIDs in column RelIDs (one per occurence of the given UserID in the original query).

Use CONCAT() instead of +:
SELECT [UserID],
STUFF((SELECT CONCAT(', ', UserID)
FROM #RelatedIDs ri
WHERE ri.UserID = t.UserID
FOR XML PATH('')
), 1, 2, '') AS RelIDs
FROM #RelatedIDs t
GROUP BY UserID;
Notice that I also changed the stuff arguments from 1, 1 to 1, 2. That is because you have a space after the comma.
Your correlation clause also suggests that the results will not be very interesting, when you get this to work.

Related

Find out Equal row set

I have a number of row base on plan and plan detail, I want to find out the same row set with other detail like plan 1 has 3 rows of data in detail table so need to find out the same rows for another plan. I have some sample data with this post may be more helpful to understand my problem. below is the Sample data Image, iwan to group by the record but not on a single row base on the full row set base on
PlanId, MinCount, MaxCount and CurrencyId
my expected data is below
I had tried to do with Some lengthy process like append all data in a single row and compare with other data, but it seems very lengthy process and takes to much time for 100 records I have an approx 20000 records in an actual database so not a good solution, please suggest me some thought
This would be trivial in MS Sql Server 2017 by using STRING_AGG.
But in 2012 one of the methods is to use the FOR XML trick.
SELECT PlanId, StairCount, MinCount, MaxCount, CurrencyId,
STUFF((
SELECT CONCAT(',', t1.AccountId)
FROM YourTable t1
WHERE t1.PlanId = t.PlanId
AND t1.StairCount = t.StairCount
AND t1.MinCount = t.MinCount
AND t1.MaxCount = t.MaxCount
AND t1.CurrencyId = t.CurrencyId
ORDER BY t1.AccountId
FOR XML PATH('')), 1, 1, '') AS AccountIdList
FROM YourTable t
GROUP BY PlanId, StairCount, MinCount, MaxCount, CurrencyId
Test here
Your desired result is rather hard to produce in SQL Server. The simplest method requires two levels of string concatenation:
with t as (
select a.account_id,
stuff( (select '[' +
convert(varchar(255), staircount) + ',' +
convert(varchar(255), mincount) + ',' +
convert(varchar(255), maxcount) + ',' +
convert(varchar(255), currencyid) +
']'
from t
where t.account_id = a.account_id
order by staircount
for xml path ('')
), 1, 1, '') as details
from (select distinct account_id from t) a
)
select d.details,
stuff( (select cast(varchar(255), account_id) + ','
from t
where t.details = d.details
for xml path ('')
), 1, 1, '') as accounts
from (select distinct details from t) d;
This isn't exactly your output, but it might be good enough for your problem.

How to Group and Include all Categories

I would like to take my raw data and group it by Name but show all the categories that each name is associated with. Hopefully the picture simplifies what I'm trying to do. Does anyone know how to accomplish this? I have considered making a pivot and then combining columns somehow, but I'm wondering if there is a more straightforward and simpler way. Thanks!
You can use the FOR XML PATH to build your CSV string. The STUFF simply removes the leading comma from the list.
SELECT Name, Categories = STUFF((
SELECT ', ' + Category
FROM dbo.YourTable
WHERE Name = x.Name
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '')
FROM dbo.YourTable AS x
GROUP BY Name;
You can use stuff and xml path as below:
Select [Name],
Categories = stuff((select Concat(',',category) from #rawdata r where r.[Name] = ou.[Name] for xml path('')), 1,1, '')
from #rawdata ou group by [Name]
If you are using SQL Server 2017 or SQL Azure then you can use String_Agg as below
Select [Name], String_Agg(category,',') from #rawdata
group by [Name]

Using distinct with stuff/for xml path('')

I'd like to put together only unique values in the concatenated string. My code is currently:
select rc.Routage
, COUNT(distinct rc.Event)
, STUFF((select ', ' + cast(rcA.Event as varchar)
from Receiving rcA
where rcA.supplier = 'user'
for xml path(''))
, 1, 1, '')
from Receiving rc
where rc.supplier = 'user'
group by rc.Routage
order by COUNT(distinct rc.Event)desc
This gives me the output I'd expect, but I would like to eliminate the duplicate values in the stuff/for xml path field.
I've tried various combinations of distinct and group by in the stuff/xml section, but can't piece it together properly.
To clarify, for COUNT(distinct rc.Event) = 2, I would like to see 2 distinct events from the stuff clause. How can I do this?
Use select distinct in the subquery:
select rc.Routage,
count(distinct rc.Event),
stuff((select distinct ', ' + cast(rcA.Event as varchar(max))
from Receiving rcA
where rcA.supplier = 'user' and
rcA.DATETIME > '20170322' and
rc.Routage = rcA.Routage
for xml path('')
), 1, 2, '')
from Receiving rc
where rc.supplier = 'user' and rc.DATETIME > '20170322'
group by rc.Routage;
Notes:
In SQL Server, never use varchar() (or related types) without a length. The default varies by context and you are (potentially) introducing a bug that is really hard to find.
You want the stuff() to remove two characters, not 1, because you have a comma followed by a space.
This formulation assumes that Event does not have XML special characters. It is easy to tweak if that is an issue.
Also, this type of query is usually faster if you eliminate the duplicates in a subquery:
select rc.Routage, rc.numEvents,
stuff((select distinct ', ' + cast(rcA.Event as varchar(max))
from Receiving rcA
where rcA.supplier = 'user' and
rcA.DATETIME > '20170322' and
rc.Routage = rcA.Routage
for xml path(''), type
).value('.', 'varchar(max)'
), 1, 2, ''
)
from (select rc.Routage, count(distinct rc.Event) as numEvents
from Receiving rc
where rc.supplier = 'user' and rc.DATETIME > '20170322'
group by rc.Routage
) rc;
Do the distinct in a subquery, before the XML processing gets anywhere near it:
select rc.Routage
, COUNT(distinct rc.Event)
, STUFF((select ', ' + cast(rcA.Event as varchar)
from (select distinct Event from Receiving a
where supplier = 'user'
and DATETIME > '20170322'
and rc.Routage=a.Routage
) rcA
for xml path(''))
, 1, 1, '')
from Receiving rc
where rc.supplier = 'user'
and rc.DATETIME > '20170322'
group by rc.Routage
order by COUNT(distinct rc.Event)desc

Transact-SQL Comma Separated Values Not Sorted

I'm trying to control the output of this query to return the values sorted from lower to higher values. As you can see on the last line of the results below I'm getting 16,15,1 as the result, but I need to get 1,15,16 as the result.
Below is the script I'm currently using. I would appreciate any help I can get. Thank you in advance.
SELECT STUFF ((SELECT ',' + CONVERT(varchar(max), UserId)
FROM MessageRecipients mr2
WHERE mr1.ConversationId = mr2.ConversationId FOR xml path('')), 1, 1,'') AS 'Result'
FROM MessageRecipients mr1
GROUP BY ConversationId
This is the result I get with the current script (column name 'result'):
1,19
1,15
16,15,1
Just add an ORDER BY to the inner SELECT:
SELECT STUFF (
(SELECT ',' + CONVERT(varchar(max), UserId)
FROM MessageRecipients mr2
WHERE mr1.ConversationId = mr2.ConversationId
ORDER BY UserId
FOR xml path(''))
, 1, 1,'') AS 'Result'
FROM MessageRecipients mr1
GROUP BY ConversationId

SQL - Combine Multiple Columns with Multiple Rows into one row

What I'm trying to do:
I have records in a SQL table where there are 5 columns and thousands of rows.
The rows share duplicate data (i.e. account number) but what makes each unique is that data in one of the columns is different.
As an example:
col1|col2|col3|col4|col5
------------------------
123|abc|456|def|789
123|abc|456|def|date
But the columns can have different values, not necessarily always in column 5.
Here's what I started with:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid]
,te.[char1]
,te.[date]
,te.[date2]
,te.[char2]
FROM D AS te
INNER JOIN D AS tue ON tue.[accountid] = te.[accountid]
WHERE tue.[accountid] = ue.[accountid]
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D AS ue
GROUP BY ue.[accountid]
But I get a monster long string that includes the duplicate rows in one column. I'm not sure what else to try so any insight would be appreciated.
If I had to guess, you have an unnecessary self join in the subquery:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid], te.[char1], te.[date], te.[date2], te.[char2]
FROM D te
WHERE te.[accountid] = ue.[accountid]
FOR XML path(''), type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D ue
GROUP BY ue.[accountid];
You might also want SELECT DISTINCT in the subquery.
Use UNION to get rid of all the duplicate values and use your FOR XML PATH on the output to append it to a single string:
SELECT TOP (15) stuff((
SELECT ', ' + CAST(te.[accountid] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char1] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date2] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char2] AS varchar(255)) FROM D
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
Untested, treat as pseudo-code to give the general idea.