Getting csv from table without any grouping SQL - sql

I have a table that contains only two columns say Name, DepartmentId as follows
CREATE TABLE #TempDepartment
(
Name NVARCHAR(50)
,DepartmentId INT
)
INSERT INTO #TempDepartment VALUES
('ABC',1)
,('ABC2',1)
,('DEF',2)
,('XYZ',3)
I am looking to retrieve list of distinct departmentId in comma seperated format. How can I achieve this in SQL? Basically there is no common group item, hence the confusion with either using STUFF or COALESCE.
Expected output is - 1,2,3

Try this query
Select Top 1 STUFF((SELECT ', ' + CAST(DepartmentId AS Varchar(20))
FROM #TempDepartment b Group By DepartmentId
FOR XML PATH('')), 1, 2, '') CSV
From #TempDepartment
Fiddle Output
O/P :
+---------+
| CSV |
+---------|
| 1, 2, 3 |
+---------+

Try this:
;WITH CTE AS
(
SELECT DISTINCT DepartmentId
FROM #TempDepartment
)
SELECT TOP (1)
STUFF((SELECT ',' + CAST(DepartmentId AS VARCHAR(10))
FROM CTE FOR XML PATH('')), 1, 1, '')
FROM CTE
Should produce the required output:
1,2,3

Related

Adding 'and' at the end of a comma separated list

Currently, I'm using the stuff function to create a comma separated list per each row.
x,y,z
What I want is to add commas for n-1 items in the list, with the final item being preceded by 'and'
x,y, and z.
For these purposes, just checking row number won't work because this list is being generated per unique Id, therefore I can't just iterate to the end of the table. Code below:
SELECT DISTINCT (sw.OwnerID)
,stuff((
SELECT DISTINCT ', ' + e.pn
FROM fct.enrtablev e
WHERE sw.OwnerID = e.OwnerId
FOR XML PATH('')), 1, 1, '') AS [Pet(s)]
A bit of a hack... AND string_agg() would be a better fit if 2017+
Here we use test the row_number() of the item count sum(1) over(), when equal this is the last item in the list
Example
Declare #YourTable table (OwnerID int,pn varchar(50))
Insert Into #YourTable values
(1,'X')
,(1,'Y')
,(1,'Z')
,(1,'Z')
,(2,'Apples')
Select Distinct
OwnerID
,stuff( ( Select case when row_number() over(order by pn) = nullif(sum(1) over() ,1)
then ', and '
else ', '
end + pn
FROM (Select distinct pn
From #YourTable
Where OwnerID = A.OwnerId
) e
Order By PN
For XML Path('')), 1, 2, '') AS [Pet(s)]
From #YourTable A
Returns
OwnerID Pet(s)
1 X, Y, and Z
2 Apples
XQUery and XML data model is based on ordered sequences. Exactly what we need.
Here is a simple solution based on XQuery and its FLWOR expression.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (OwnerID int, pn VARCHAR(50));
INSERT INTO #tbl (OwnerID, pn) VALUES
(1,'X'),
(1,'Y'),
(1,'Z'),
(2,'Apples');
-- DDL and sample data population, end
SELECT p.OwnerID
, (SELECT *
FROM #tbl AS c
WHERE c.OwnerID = p.OwnerID
FOR XML PATH('r'), TYPE, ROOT('root')
).query('
for $x in /root/r/pn/text()
return if ($x is (/root/r[last()]/pn/text())[1]) then
if (count(/root/r) gt 1) then concat("and ", $x) else string($x)
else concat($x, ",")
').value('.', 'VARCHAR(MAX)') AS Result
FROM #tbl AS p
GROUP BY p.OwnerID;
Output
+---------+----------------+
| OwnerID | Result |
+---------+----------------+
| 1 | X, Y, and Z |
| 2 | Apples |
+---------+----------------+
You can achieve this using ORDER BY and count.
Declare #YourTable table (OwnerID int,pn varchar(50))
Insert Into #YourTable values
(1,'X')
,(1,'Y')
,(1,'Z')
,(2,'Apples')
;WITH CTE_OwnerIdRank as
(
SELECT ownerid, pn, row_number() over (order by ownerId) as totalrn,
count(*) over(partition by ownerid order by ownerid) as ownercnt
from #yourtable
)
SELECT distinct OwnerId,
stuff((
SELECT ', ' + CASE WHEN c.totalrn = c.ownercnt then CONCAT(' and ',c.pn) else c.pn end
FROM CTE_OwnerIdRank as c
WHERE c.OwnerID = o.OwnerId
order by c.totalrn
FOR XML PATH('')), 1, 1, '') AS [Pet(s)]
from #yourtable as o
OwnerId
Pet(s)
1
X, Y, and Z
2
Apples

STUFF doesn't work well with NULL Values and Grouping

I have table with below schema and data.
CREATE TABLE [dbo].[SearchTest]
(
[DocumentNumber] [int] NOT NULL,
[AlphaNumeric] [nvarchar](50) NULL,
[Integers] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (1, N'abc', 1)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (2, N'abc', 1)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (3, N'bcd', 2)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (4, N'bcd', 2)
GO
Table data:
I would like to do grouping using Alphanumeric and Integers column and get the DocumentNumber as comma separated value in my final result.
My final result should look like this,
Here is my query that gives the above output,
SELECT *
FROM
(SELECT
STUFF((SELECT ', ' + CAST(DocumentNumber AS VARCHAR(10)) [text()]
FROM SearchTest
WHERE AlphaNumeric = Result.Alphanumeric
OR Integers = Result.Integers
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') DocumentNumbers,
COUNT(DocumentNumber) TotalDocuments,
Result.AlphaNumeric,
Result.Integers
FROM
(SELECT *
FROM SearchTest
WHERE AlphaNumeric LIKE '%b%' OR Integers = 1) AS Result
GROUP BY
Result.AlphaNumeric, Result.Integers) AS Final
However the above query breaks if I have null values in Integers column.
For example, if I have NULL value in my integer columns as shown here:
Now my query breaks and I get the wrong results in my stuff query as shown below
Grouping works fine in the above query but STUFF part which gives DocumentNumbers gives wrong result. In this case it has be 2 in first row and 1 in second row.
Here is the expected result:
| DocumentNumbers| TotalDocuments| AlphaNumeric | Integers |
+----------------+---------------+---------------+---------------+
| 2 | 1 | abc | NULL |
| 1 | 1 | abc | 1 |
| 3, 4 | 2 | bcd | 2 |
Please assist on where I'm going wrong
You need to change the WHERE clause of the inner query to a) use AND instead of OR and b) to check for NULLs too.
SELECT stuff((SELECT concat(', ', documentnumber)
FROM searchtest st2
WHERE (st2.alphanumeric = st1.alphanumeric
OR st2.alphanumeric IS NULL
AND st1.alphanumeric IS NULL)
AND (st2.integers = st1.integers
OR st2.integers IS NULL
AND st1.integers IS NULL)
FOR XML PATH('')),
1,
2,
'') documentnumbers,
count(*) totaldocuments,
alphanumeric,
integers
FROM searchtest st1
WHERE st1.alphanumeric LIKE '%b%'
OR st1.integers = 1
GROUP BY st1.alphanumeric,
st1.integers;
Following #GordonLinoff comments in the question. This can be easily achieved using STRING_AGG() provided you're using SQL Server 2017 and above. This simplifies the query as well.
Query:
SELECT *
FROM
(SELECT
STRING_AGG(Result.DocumentNumber, ', ') DocumentNumbers,
COUNT(DocumentNumber) TotalDocuments,
Result.AlphaNumeric,
Result.Integers
FROM
(SELECT *
FROM SearchTest
WHERE AlphaNumeric LIKE '%b%' OR Integers = 1) AS Result
GROUP BY
Result.AlphaNumeric, Result.Integers) AS Final
Expected Output:
I had edited the code now:
You can use the below code to attain this:
SELECT S.DocumentNumbers,COUNT(S.DocumentNumbers) AS TotalDocuments,S.AlphaNumeric,S.Integers FROM
(SELECT COALESCE(Stuff((SELECT ', ' + CAST(DocumentNumber AS VARCHAR(10))
FROM SearchTest T1
WHERE T1.AlphaNumeric=T2.AlphaNumeric AND T1.Integers=T2.Integers
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,' ')
,CAST (T2.DocumentNumber AS VARCHAR(20))) AS DocumentNumbers,T2.AlphaNumeric,T2.Integers
FROM SearchTest T2) S
GROUP BY DocumentNumbers,S.AlphaNumeric,S.Integers
ORDER BY S.Integers

How to concatenate strings in SQL Server, and sort/ order by a different column?

I've seen many examples of concatenating strings in SQL Server, but if they worry about sorting, it's always by the column being concatenated.
I need to order the values based on data in a different fields.
Sample table:
ClassID | StudentName | SortOrder
-----------------------------
A |James |1
A |Janice |3
A |Leonard |2
B |Luke |2
B |Leia |1
B |Artoo |3
And the results I'd like to get are:
ClassID |StudentName
--------------------------------
A |James, Leonard, Janice
B |Leia, Luke, Artoo
How can this be done in SQL Server 2016?
(I'm looking forward to STRING_AGG in 2017, but we're not there yet...)
Thanks!
Here you go:
SELECT
s1.ClassID
, STUFF((SELECT
',' + s2.StudentName
FROM dbo.Student AS s2
WHERE s1.classID = s2.ClassID
ORDER BY s2.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNames
FROM dbo.Student AS s1
GROUP BY s1.ClassID
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE MyTable(ClassID varchar(255),StudentName varchar(255),SortOrder int)
INSERT INTO MyTable(ClassID,StudentName,SortOrder)VALUES('A','James',1),('A','Janice',3),('A','Leonard',2),
('B','Luke',2),('B','Lela',1),('B','Artoo',3)
Query 1:
SELECT
t.ClassID
, STUFF((SELECT
',' + t1.StudentName
FROM MyTable t1
WHERE t.classID = t1.ClassID
ORDER BY t1.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNamesConcat
FROM MyTable AS t
GROUP BY t.ClassID
Results:
| ClassID | StudentNamesConcat |
|---------|----------------------|
| A | James,Leonard,Janice |
| B | Lela,Luke,Artoo |
Here the query
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP TABLE #Temp;
CREATE TABLE #Temp(ClassId varchar(10),studName varchar(100),SortOrder int)
INSERT INTO #Temp(ClassId , studName, SortOrder)
SELECT 'A','James',1 UNION ALL
SELECT 'A','Janice',3UNION ALL
SELECT 'A','Leonard',2 UNION ALL
SELECT 'B','Luke',2 UNION ALL
SELECT 'B','Leia',1 UNION ALL
SELECT 'B','Artoo',3
-- select * from #Temp
select
distinct
stuff((
select ',' + u.studName
from #Temp u
where u.studName = studName and U.ClassId = L.ClassId
order by u.SortOrder
for xml path('')
),1,1,'') as userlist,ClassId
from #Temp L
group by ClassId
You can try using PIVOT also. This will support even older version of SQL server.
Only limitation : you should know the maximum SortOrder value. Below code will work for SortOrder <=20 of any ClassID
SELECT ClassID, ISNULL([1],'') +ISNULL(', '+[2],'')+ISNULL(', '+[3],'')+ISNULL(', '+
[4],'')+ISNULL(', '+[5],'')+ISNULL(', '+[6],'')+ISNULL(', '+[7],'')+ISNULL(', '+
[8],'')+ISNULL(', '+[9],'')+ISNULL(', '+[10],'')+ISNULL(', '+[11],'')+ISNULL(', '+
[12],'')+ISNULL(', '+[13],'')+ISNULL(', '+[14],'')+ISNULL(', '+[15],'')+ISNULL(', '+
[16],'')+ISNULL(', '+[17],'')+ISNULL(', '+[18],'')+ISNULL(', '+[19],'')+ISNULL(', '+
[20],'') AS StudentName
FROM
(SELECT SortOrder,ClassID,StudentName
FROM [Table1] A
) AS SourceTable
PIVOT
(
MAX(StudentName)
FOR SortOrder IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20])
) AS PivotTable

Sql query for merge two rows into one row if id is same

I have table named TICKET_LAB
I have table named TICKET_LAB
as shown
as shown
TICKET_LAB table
and table named test
and table named test
as shown
as shown
Test table:
I need a SQL query to get the result like that :
I need a SQL query to get the result like that :
TLAB_TICKETID | TEST_NAME
------------- | -------------
29 | blood, stool,...etc
any idea
In SQL Server it may looks like this:
CREATE TABLE #TICKET_LAB(Id int)
INSERT INTO #TICKET_LAB VALUES(1), (2)
CREATE TABLE #TEST(TicketId int, Name nvarchar(100))
INSERT INTO #TEST VALUES(1, 'blood'), (1, 'etc.'), (2, 'test')
SELECT Id, STUFF((
SELECT ', ' + t.Name FROM #TEST t WHERE tl.Id = t.TicketId FOR XML PATH('')), 1, 2, ''
)
FROM #TICKET_LAB tl
SELECT tckt.TLAB_TICKETID, STUFF(
(SELECT ',' + TEST_NAME
FROM [hos].[dbo].[test]
WHERE tckt.TLAB_TESTID = Test_ID
FOR XML PATH (''))
, 1, 1, '')
FROM [hos].[dbo].[test] tst
JOIN [hos].[dbo].[Ticket_Lab] tckt
on tst.Test_ID = tckt.TLAB_TESTID
GROUP BY tckt.TLAB_TICKETID
I think this should work.

Combine two rows into one row. like average function but instead it will concat

I'm using SQL Server 2000.
What I want to do is like average function but instead it will concat.
Is there a way that I can do that?
For example I have this data.
Name | Score
Name1 | 50
Name1 | 70
and the output should be like this.
Name | Score
Name1 | 50,70
Use below query for your reference.
Query
Select main.doctorID,
Left(Main.submain,Len(Main.submain)-1) As 'Title'
From
(
Select distinct ST2.doctorID,
(
Select convert(varchar,ST1.encounterid) + ',' AS [text()]
From dbo.enc ST1
Where ST1.doctorID = ST2.doctorID
ORDER BY ST1.doctorID
For XML PATH ('')
) submain
From dbo.enc ST2
) [Main]
If you can use CLR, look at this example :
https://msdn.microsoft.com/en-us/library/ms165055%28v=vs.90%29.aspx
It provides a custom aggregate that concatenates values, which results in very clean code.
You can do it by this simple query.
Select Name, (Select SUBSTRING((SELECT ', '+Score from TableName for XML
Path('')) ,2,8000)) from TableName
You will have result like
ColumnName | val1, val2, ....
check this.
INSERT INTO #T VALUES
('name1', 50),
('name1', 70)
SELECT * FROM #T
Select name ,
STUFF((SELECT ',' + cast( score as varchar(50)) FROM #T WHERE (
name=Result.name) FOR XML PATH ('')),1,1,'') AS BATCHNOLIST
From #T AS Result
GROUP BY name
Can I Comma Delimit Multiple Rows Into One Column?
Query
SELECT Name,
(SELECT SUBSTRING((SELECT ', '+CAST(Score AS VARCHAR(MAX)) FROM my_table FOR XML Path('')) ,2,1000)) AS Score
FROM my_table
GROUP BY name;
Fiddle for reference
If you are using SQL Server 2000, then try to create a function as follows.
CREATE TABLE my_table(name VARCHAR(50),score INT);
INSERT INTO my_table VALUES('Name1',50);
INSERT INTO my_table VALUES('Name1',70);
Function
CREATE FUNCTION commaseparated(#name VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #score VARCHAR(MAX)
SELECT #score = COALESCE(#score + ', ', '') + CAST(score AS VARCHAR(MAX))
FROM my_table
WHERE name = #name
RETURN #score
END
SELECT
name,
score = dbo.commaseparated(name)
FROM my_table
GROUP BY name;