Need comma separated value in table in SQL Server 2014 [duplicate] - sql-server

This question already has answers here:
get a comma delimited string from rows [duplicate]
(2 answers)
Closed 4 years ago.
I have a table like this.
Create table #temp
(
id int,
firstname varchar(50),
lastname varchar(50)
)
insert into #temp (id, firstname, lastname)
select 1,'mit','jain'
insert into #temp (id, firstname, lastname)
select 1,'mit','jain1'
insert into #temp (id, firstname, lastname)
select 1,'mit','jain2'
insert into #temp (id, firstname, lastname)
select 2,'mit','jain3'
insert into #temp (id, firstname, lastname)
select 2,'mit','jain4'
insert into #temp (id, firstname, lastname)
select 1,'mit','jain5'
insert into #temp (id, firstname, lastname)
select 1,'mit','jain6'
I want the table to be shown as below
id firstname lastname
----------------------------------------------
1 mit jain,jain1,jain2,jain5,jain6
2 mit jain2,jain4
I have tried the query as below
select
id, firstname,
substring((Select ', '+tc1.lastname AS [text()]
From #temp tc1
Inner Join #temp c1 On c1.id = tc1.id
Where tc1.firstname = c1.firstname
Order BY tc1.lastname
For Xml Path('')), 2, 1000) 'LastName1'
from #temp
group by id, firstname
But it's not working. Please help me out

You're part of the way there. The tradition method is using STUFF:
SELECT t.id, t.firstname,
STUFF((SELECT ', ' + sq.lastname
FROM #temp sq
WHERE sq.id = t.id
AND sq.firstname = t.firstname
ORDER BY sq.lastname
FOR XML PATH('')),1,1,'') AS lastname
FROM #temp t
GROUP BY t.id, t.firstname;
There are lots of answers on SO already on how to do this though, but you have shown effort. :)

Related

Comapre sqlserver table with the query results

I have a table 'A' with few columns say - ID, firstname,lastname,address and phonenumber.
I am populating this table on a daily basis using a query that joins few tables.
Now before loading the table, I need to check my query results against the table data and see if the data is already present.
If my table data and query results matches, then no need to insert the record.
if my table data and query result doesn't match then need to insert the record.
If the table data and the query result has updated record, then the data has to be inserted.
that is, If the Id,lastname,firstname, address are all same and only the PhoneNumber is changes in my query result. Then this record should also be inserted in the table A.
I tried a query to identify the Point -3, But I'm stuck how to proceed further and make thhose records to be inserted in table A.
SELECT DISTINCT *
FROM
(
SELECT * FROM
( SELECT [ID],FirstName, LastName,[PhoneNumber],[ExpDate] FROM [SCRATCH].[dbo].[A]
UNION ALL
SELECT D.[ID],FirstName, LastName,,[PhoneNumber],[ExpDate] FROM
S JOIN D ON D.ID = S.ID) Tbls
GROUP BY [ID],FirstName, LastName,[PhoneNumber],[ExpDate]
HAVING COUNT(*)<2) Diff
To get a list of rows that don't match between two tables - you can use EXCEPT. For example:
SELECT [ID],FirstName, LastName,[PhoneNumber],[ExpDate] FROM [SCRATCH].[dbo].[A]
Except
SELECT D.[ID],FirstName, LastName,,[PhoneNumber],[ExpDate] FROM S
From this - we can then construct an insert statement, here is an example with data:
Declare #tableS Table (ID int, FirstName varchar(30), LastName varchar(30), PhoneNumber varchar(12), ExpDate date);
Insert Into #tableS (ID, FirstName, LastName, PhoneNumber, ExpDate)
Values (1, 'Test', 'Testing', '111-111-1111', '2021-03-01');
Declare #tableA Table (ID int, FirstName varchar(30), LastName varchar(30), PhoneNumber varchar(12), ExpDate date)
Insert Into #tableA (ID, FirstName, LastName, PhoneNumber, ExpDate)
Values (2, 'Test', 'Testing', '222-222-2222', '2021-03-01')
, (3, 'Test', 'Testing', '333-333-3333', '2021-03-01');
--==== Insert new/changed rows into #tableS
Insert Into #tableS (ID, FirstName, LastName, PhoneNumber, ExpDate)
Select ID, FirstName, LastName, PhoneNumber, ExpDate From #tableA
Except
Select ID, FirstName, LastName, PhoneNumber, ExpDate From #tableS;
Select * From #tableS;
Insert Into #tableA (ID, FirstName, LastName, PhoneNumber, ExpDate)
Values (1, 'Test', 'Testing', '111-222-3333', '2021-03-02')
, (4, 'Test', 'Testing', '444-444-4444', '2021-03-04');
--==== Insert new/changed rows into #tableS
Insert Into #tableS (ID, FirstName, LastName, PhoneNumber, ExpDate)
Select ID, FirstName, LastName, PhoneNumber, ExpDate From #tableA
Except
Select ID, FirstName, LastName, PhoneNumber, ExpDate From #tableS;
Select * From #tableS;
You probably will need to adjust the columns used with EXCEPT so you are not comparing the ID and the ExpDate - but this should get you started.

How to get one column of one record in SQL query?

I try to get full name of #MechanicExpertTable table by select query but get error that
Incorrect syntax near the keyword 'SELECT'.
My code:
DECLARE #MechanicExpertTable AS TABLE
(
Id INT,
FirstName NVARCHAR(128),
LastName NVARCHAR(128)
);
INSERT INTO #MechanicExpertTable
SELECT
PROFILE.Id,
PROFILE.FirstName,
PROFILE.LastName
FROM
EstimatedRialMechanicExpert
INNER JOIN
PROFILE ON EstimatedRialMechanicExpert.ProfileId = PROFILE.Id
WHERE
EstimatedRialId = #id
DECLARE #MechanicExpert1 NVARCHAR(128) =
SELECT TOP(1)
ROW_NUMBER() OVER(ORDER BY Id ASC) AS rownumber,
#MechanicExpertTable.FirstName + ' ' + #MechanicExpertTable.LastName
FROM
#MechanicExpertTable
WHERE
rownumber = 3
How to fix this?
If you want to get the full name for а specific position, try with the following example. In your case, ROW_NUMBER() is used without PARTITION BY, so TOP(1) is not necessary.
Input:
DECLARE #MechanicExpertTable AS TABLE (
Id INT,
FirstName NVARCHAR(128),
LastName NVARCHAR(128)
);
INSERT INTO #MechanicExpertTable
(Id, FirstName, LastName)
VALUES
(1, 'FirstName1', 'LastName1'),
(2, 'FirstName2', 'LastName2'),
(6, 'FirstName6', 'LastName6'),
(7, 'FirstName7', 'LastName7'),
(9, 'FirstName9', 'LastName9')
T-SQL:
DECLARE #MechanicExpert1 NVARCHAR(128)
SELECT TOP(1) #MechanicExpert1 = FullName
FROM (
SELECT
ROW_NUMBER() OVER(ORDER BY Id ASC) AS rownumber,
FirstName + ' ' + LastName AS FullName
FROM #MechanicExpertTable
) t
WHERE rownumber = 3
PRINT #MechanicExpert1
Output:
FirstName6 LastName6
Missing parentheses. Add () for your sub query as and the code should be as below-
DECLARE #MechanicExpert1 NVARCHAR(128) =
(
SELECT TOP 1 T
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY Id ASC) AS rownumber,
#MechanicExpertTable.FirstName + ' ' + #MechanicExpertTable.LastName AS T
FROM #MechanicExpertTable
)A WHERE rownumber = 3
)
Note: I have added TOP 1 in the selection as it was available in your script. Basically when you are filtering with RowNumber = 3 there are no chance of coming multiple rows. You can remove "TOP 1" from the script.

SQL Server group by count eliminate duplicates [duplicate]

How do I get:
id Name Value
1 A 4
1 B 8
2 C 9
to
id Column
1 A:4, B:8
2 C:9
No CURSOR, WHILE loop, or User-Defined Function needed.
Just need to be creative with FOR XML and PATH.
[Note: This solution only works on SQL 2005 and later. Original question didn't specify the version in use.]
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
If it is SQL Server 2017 or SQL Server Vnext, SQL Azure you can use STRING_AGG as below:
SELECT id, STRING_AGG(CONCAT(name, ':', [value]), ', ')
FROM #YourTable
GROUP BY id
using XML path will not perfectly concatenate as you might expect... it will replace "&" with "&" and will also mess with <" and ">
...maybe a few other things, not sure...but you can try this
I came across a workaround for this... you need to replace:
FOR XML PATH('')
)
with:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
...or NVARCHAR(MAX) if thats what youre using.
why the hell doesn't SQL have a concatenate aggregate function? this is a PITA.
I ran into a couple of problems when I tried converting Kevin Fairchild's suggestion to work with strings containing spaces and special XML characters (&, <, >) which were encoded.
The final version of my code (which doesn't answer the original question but may be useful to someone) looks like this:
CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
Rather than using a space as a delimiter and replacing all the spaces with commas, it just pre-pends a comma and space to each value then uses STUFF to remove the first two characters.
The XML encoding is taken care of automatically by using the TYPE directive.
Another option using Sql Server 2005 and above
---- test data
declare #t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert #t select 1125439 ,'CKT','Approved'
insert #t select 1125439 ,'RENO','Approved'
insert #t select 1134691 ,'CKT','Approved'
insert #t select 1134691 ,'RENO','Approved'
insert #t select 1134691 ,'pn','Approved'
---- actual query
;with cte(outputid,combined,rn)
as
(
select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
from #t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
Install the SQLCLR Aggregates from http://groupconcat.codeplex.com
Then you can write code like this to get the result you asked for:
CREATE TABLE foo
(
id INT,
name CHAR(1),
Value CHAR(1)
);
INSERT INTO dbo.foo
(id, name, Value)
VALUES (1, 'A', '4'),
(1, 'B', '8'),
(2, 'C', '9');
SELECT id,
dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM dbo.foo
GROUP BY id;
Eight years later... Microsoft SQL Server vNext Database Engine has finally enhanced Transact-SQL to directly support grouped string concatenation. The Community Technical Preview version 1.0 added the STRING_AGG function and CTP 1.1 added the WITHIN GROUP clause for the STRING_AGG function.
Reference: https://msdn.microsoft.com/en-us/library/mt775028.aspx
SQL Server 2005 and later allow you to create your own custom aggregate functions, including for things like concatenation- see the sample at the bottom of the linked article.
This is just an addition to Kevin Fairchild's post (very clever by the way). I would have added it as a comment, but I don't have enough points yet :)
I was using this idea for a view I was working on, however the items I was concatinating contained spaces. So I modified the code slightly to not use spaces as delimiters.
Again thanks for the cool workaround Kevin!
CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT )
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9)
SELECT [ID],
REPLACE(REPLACE(REPLACE(
(SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A
FROM #YourTable
WHERE ( ID = Results.ID )
FOR XML PATH (''))
, '</A><A>', ', ')
,'<A>','')
,'</A>','') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
An example would be
In Oracle you can use LISTAGG aggregate function.
Original records
name type
------------
name1 type1
name2 type2
name2 type3
Sql
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
Result in
name type
------------
name1 type1
name2 type2; type3
This kind of question is asked here very often, and the solution is going to depend a lot on the underlying requirements:
https://stackoverflow.com/search?q=sql+pivot
and
https://stackoverflow.com/search?q=sql+concatenate
Typically, there is no SQL-only way to do this without either dynamic sql, a user-defined function, or a cursor.
Just to add to what Cade said, this is usually a front-end display thing and should therefore be handled there. I know that sometimes it's easier to write something 100% in SQL for things like file export or other "SQL only" solutions, but most of the times this concatenation should be handled in your display layer.
Don't need a cursor... a while loop is sufficient.
------------------------------
-- Setup
------------------------------
DECLARE #Source TABLE
(
id int,
Name varchar(30),
Value int
)
DECLARE #Target TABLE
(
id int,
Result varchar(max)
)
INSERT INTO #Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO #Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO #Source(id, Name, Value) SELECT 2, 'C', 9
------------------------------
-- Technique
------------------------------
INSERT INTO #Target (id)
SELECT id
FROM #Source
GROUP BY id
DECLARE #id int, #Result varchar(max)
SET #id = (SELECT MIN(id) FROM #Target)
WHILE #id is not null
BEGIN
SET #Result = null
SELECT #Result =
CASE
WHEN #Result is null
THEN ''
ELSE #Result + ', '
END + s.Name + ':' + convert(varchar(30),s.Value)
FROM #Source s
WHERE id = #id
UPDATE #Target
SET Result = #Result
WHERE id = #id
SET #id = (SELECT MIN(id) FROM #Target WHERE #id < id)
END
SELECT *
FROM #Target
Let's get very simple:
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
Replace this line:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
With your query.
You can improve performance significant the following way if group by contains mostly one item:
SELECT
[ID],
CASE WHEN MAX( [Name]) = MIN( [Name]) THEN
MAX( [Name]) NameValues
ELSE
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
END
FROM #YourTable Results
GROUP BY ID
didn't see any cross apply answers, also no need for xml extraction. Here is a slightly different version of what Kevin Fairchild wrote. It's faster and easier to use in more complex queries:
select T.ID
,MAX(X.cl) NameValues
from #YourTable T
CROSS APPLY
(select STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = T.ID)
FOR XML PATH(''))
,1,2,'') [cl]) X
GROUP BY T.ID
Using the Stuff and for xml path operator to concatenate rows to string :Group By two columns -->
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',5)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
-- retrieve each unique id and name columns and concatonate the values into one column
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES EACH APPLICATION : VALUE SET
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
SELECT
[ID],[Name] , --these are acting as the group by clause
STUFF((
SELECT ', '+ CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES THE VALUES FOR EACH ID NAME COMBINATION
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID, name
DROP TABLE #YourTable
Using Replace Function and FOR JSON PATH
SELECT T3.DEPT, REPLACE(REPLACE(T3.ENAME,'{"ENAME":"',''),'"}','') AS ENAME_LIST
FROM (
SELECT DEPT, (SELECT ENAME AS [ENAME]
FROM EMPLOYEE T2
WHERE T2.DEPT=T1.DEPT
FOR JSON PATH,WITHOUT_ARRAY_WRAPPER) ENAME
FROM EMPLOYEE T1
GROUP BY DEPT) T3
For sample data and more ways click here
If you have clr enabled you could use the Group_Concat library from GitHub
Another example without the garbage: ",TYPE).value('(./text())[1]','VARCHAR(MAX)')"
WITH t AS (
SELECT 1 n, 1 g, 1 v
UNION ALL
SELECT 2 n, 1 g, 2 v
UNION ALL
SELECT 3 n, 2 g, 3 v
)
SELECT g
, STUFF (
(
SELECT ', ' + CAST(v AS VARCHAR(MAX))
FROM t sub_t
WHERE sub_t.g = main_t.g
FOR XML PATH('')
)
, 1, 2, ''
) cg
FROM t main_t
GROUP BY g
Input-output is
************************* -> *********************
* n * g * v * * g * cg *
* - * - * - * * - * - *
* 1 * 1 * 1 * * 1 * 1, 2 *
* 2 * 1 * 2 * * 2 * 3 *
* 3 * 2 * 3 * *********************
*************************
I used this approach which may be easier to grasp. Get a root element, then concat to choices any item with the same ID but not the 'official' name
Declare #IdxList as Table(id int, choices varchar(max),AisName varchar(255))
Insert into #IdxLIst(id,choices,AisName)
Select IdxId,''''+Max(Title)+'''',Max(Title) From [dbo].[dta_Alias]
where IdxId is not null group by IdxId
Update #IdxLIst
set choices=choices +','''+Title+''''
From #IdxLIst JOIN [dta_Alias] ON id=IdxId And Title <> AisName
where IdxId is not null
Select * from #IdxList where choices like '%,%'
For all my healthcare folks out there:
SELECT
s.NOTE_ID
,STUFF ((
SELECT
[note_text] + ' '
FROM
HNO_NOTE_TEXT s1
WHERE
(s1.NOTE_ID = s.NOTE_ID)
ORDER BY [line] ASC
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,
1,
2,
'') AS NOTE_TEXT_CONCATINATED
FROM
HNO_NOTE_TEXT s
GROUP BY NOTE_ID

How to merge multiple rows in one single column field with given condition in SQL SERVER 2008 [duplicate]

This question already has answers here:
Combine multiple results in a subquery into a single comma-separated value
(10 answers)
Closed 9 years ago.
I am using SQL Server 2008 and I need to implement this :->
I get a result like this :
Table:-sql query ..............
acc_no name
001-000001 John
001-000001 Bob
001-000001 James
001-000002 Sam
001-000002 Bin
001-000002 Dus
So, the condition is that; multiple persons can have same acc_no. So i want following result:
acc_no name
001-000001 John,Bob,James
001-000002 Sam,Bin,Dus
There are other conditions for displaying the results but I got stuck in displaying this format.
How about something like
DECLARE #Table TABLE(
acc_no VARCHAR(50),
name VARCHAR(50)
)
INSERT INTO #Table (acc_no,name) SELECT '001-000001', 'John'
INSERT INTO #Table (acc_no,name) SELECT '001-000001', 'Bob'
INSERT INTO #Table (acc_no,name) SELECT '001-000001', 'James'
INSERT INTO #Table (acc_no,name) SELECT '001-000002', 'Sam'
INSERT INTO #Table (acc_no,name) SELECT '001-000002', 'Bin'
INSERT INTO #Table (acc_no,name) SELECT '001-000002', 'Dus'
--Concat
SELECT t.acc_no,
stuff(
(
select ',' + t1.name
from #Table t1
where t1.acc_no = t.acc_no
order by t1.name
for xml path('')
),1,1,'') Concats
FROM #Table t
GROUP BY t.acc_no
SQL Fiddle DEMO
Try this one -
DECLARE #temp TABLE
(
ID VARCHAR(20)
, Name VARCHAR(50)
)
INSERT INTO #temp (ID, Name)
VALUES
('001-000001', 'John'),
('001-000001', 'Bob'),
('001-000001', 'James'),
('001-000002', 'Sam'),
('001-000002', 'Bin'),
('001-000002', 'Dus')
SELECT t.ID, STUFF((
SELECT ', ' + t2.Name
FROM #temp t2
WHERE t.ID = t2.ID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
FROM (
SELECT DISTINCT ID
FROM #temp
) t
Output -
ID
-------------------- -------------------
001-000001 John, Bob, James
001-000002 Sam, Bin, Dus
select acc_no,stuff((SELECT distinct ', ' + cast(name as varchar(10))
FROM yourtable t2
where t2.acc_no = t1.acc_no
FOR XML PATH('')),1,1,'')
from yourtable t1
group by acc_no

Puzzling xml select syntax in Sql Server

In the below sql code, what does T(C) mean? What is T and what is C?
declare #employeeData xml --this would be your XML input parameter
set #employeeData = '<employeeData>
<employee LastName="Smith" FirstName="Randolph" EmployeeID="1234567"/>
</employeeData>'
declare #xmlTable table (LastName nvarchar(255), FirstName nvarchar(255), EmployeeID int)
insert into #xmlTable (LastName, FirstName, EmployeeID)
select
C.value('#LastName','nvarchar(255)') as LastName,
C.value('#FirstName','nvarchar(255)') as FirstName,
C.value('#EmployeeID','int') as EmployeeID
from
#employeeData.nodes('/employeeData/employee') T(C)
select * from #xmlTable
Check MSDN: http://msdn.microsoft.com/en-us/library/ms188282.aspx
T - Table
C - Column

Resources