Transact-SQL Comma Separated Values Not Sorted - sql

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

Related

SQL how to put values on one line

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.

Need distinct comma delimited product values order by date

I have below table structure
create TABLE PRODUCTDeatils
(
Product varchar(50),
Date Datetime
)
I want an output where i get comma separated list with distinct values but order by date.
I first split the values by below query
SELECT DISTINCT
LTRIM(RTRIM(value)) AS Product, date
INTO #tmp3
FROM PRODUCTDeatils
CROSS APPLY STRING_SPLIT(LTRIM(RTRIM(Product)), ',')
SELECT *
FROM #tmp3
ORDER BY date
Then I used below two queries to achieve it but not successful.
Query #1:
DECLARE #cols AS NVARCHAR(MAX);
SELECT #COLS = substring(list, 1, LEN(list) - 1)
FROM
(SELECT
list = (SELECT DISTINCT Product + ';'
FROM #tmp3
ORDER BY Product + ';'
FOR XML PATH(''), TYPE).value('.', 'nvarchar(MAX)')) AS T
SELECT #COLS
Query #2:
SELECT
STUFF((SELECT '; ' + Product
FROM
(SELECT DISTINCT TOP 100
Product,date
FROM #tmp3) x
ORDER BY date
FOR XML PATH('')), 1, 2, '') Text
I want output like above but bat should only come once.
In the more recent versions of SQL Server, you should just use string_agg():
SELECT STRING_AGG(Product, ',') WITHIN GROUP (ORDER BY date)
FROM (SELECT LTRIM(RTRIM(s.value)) AS Product, MIN(date) as date
FROM ProductDetails pd CROSS APPLY
STRING_SPLIT(LTRIM(RTRIM(pd.Product)), ',') s
GROUP BY LTRIM(RTRIM(s.value))
) p
Here is a db<>fiddle.
You can add row_number and select only one value per product.
SELECT DISTINCT
LTRIM(RTRIM(value)) AS Product, date, ROW_NUMBER() over (partition by trim(Value) order by date) rn
INTO #tmp3
FROM PRODUCTDeatils
CROSS APPLY STRING_SPLIT(LTRIM(RTRIM(Product)), ',')
SELECT
STUFF((SELECT '; ' + Product
FROM
(SELECT DISTINCT TOP 100
Product,date
FROM #tmp3 where rn = 1) x
ORDER BY date
FOR XML PATH('')), 1, 2, '') Text
Please find the db<>fiddle here.

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.

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

SQL Server: how to remove last comma after combining rows using XML Path

I found a way to combine multiple row's into one row which is comma separated but now I would like to remove the last comma.
CREATE TABLE supportContacts
(
id int identity primary key,
type varchar(20),
details varchar(30)
);
INSERT INTO supportContacts (type, details)
VALUES ('Email', 'admin#sqlfiddle.com'),
('Twitter', '#sqlfiddle');
This query combines types, but I want to now remove the last comma:
SELECT top (2)
type + ', ' AS 'data()'
FROM
supportContacts
ORDER BY
type DESC
FOR XML PATH('')
This is the current result:
Twitter, Email,
While you already have an answer, another common idiom that you'll see is:
select stuff((
SELECT top (2)
', ' type AS 'data()'
FROM
supportContacts
ORDER BY
type DESC
FOR XML PATH('')
), 1, 2, '')
This says "take the result of the select and replace the two characters starting at position 1 with a zero-length string".
This works for me->
1.Inserting comma Before Data
2.Using Stuff to Remove it
select (stuff((
SELECT ', '+ Name AS 'data()'
FROM Table_1
FOR XML PATH('')),
Count('ID')
, 1, ' '))as Result
declare #BigStrRes8K nvarchar(4000)
SELECT #BigStrRes8K = ( SELECT top (2) [type] + ', ' AS 'data()'
FROM supportContacts
ORDER BY type DESC
FOR XML PATH('') )
SELECT LEFT(RTRIM(#BigStrRes8K), ( LEN(RTRIM(#BigStrRes8K))) - 1) as FinalNoComma
I would never do this where I controlled the render code. I would teach the caller to handle the trailing comma. Also you have to allow for nulls and the 4K or 8K limit of SQL rows