SQL Server - Get substring form a string - sql

I have a string coming like '1234_XXXX_RHL_PQR' and in output I want to pull character coming after 'XXXX_' that is 'RHL'.
I would always have 'XXXX' in string and my task is to get string after 'XXXX_'

Try This
DECLARE #Table AS TABLE (Data Nvarchar(100))
INSERT INTO #Table
SELECT '1234_XXXX_RHL_PQR'
SELECT Data,CAST('<S>'+REPLACE(Data,'_','</S><S>')+'</S>' AS XML).value('/S[3]','nvarchar(1000)') AS ReqData
FROM #Table
Result
Data ReqData
----------------------------
1234_XXXX_RHL_PQR RHL
DEMO : http://rextester.com/PXHSDZ80426

Try the following:
select
right(yourfield,len(yourfield)-3-patindex('%XXXX%',yourfield))
from yourtable

I would use substring() with charindex() function
select col, substring(col, charindex('XXXX_', col)+5, len(col)) as Ncol
from table t;

First Create Table-Value-Function
CREATE FUNCTION [dbo].[Udf_GetReqString](#IputData Varchar(200))
RETURNS #OutTable TABLE
(
ActualData varchar(200),
ReqData varchar(200)
)
AS
BEGIN
DECLARE #Table AS TABLE (Data Nvarchar(100))
INSERT INTO #Table
SELECT #IputData
INSERT INTO #OutTable
SELECT Data,SUBSTRING(DatReq,-1,CHARINDEX('_',Data )) AS DatReq
FROM
(
SELECT Data,SUBSTRING(Data,CHARINDEX('X_',Data )+2,LEN(Data) ) AS DatReq
FROM #Table
)dt
RETURN
END
Sample And Call Function
DECLARE #Table AS TABLE (Data Nvarchar(100))
INSERT INTO #Table
SELECT '1234_XXXX_SHL_PQR' UNION ALL
SELECT '1234_XXXX_RHL_PQR' UNION ALL
SELECT '1234_DDHDFD_XXXX_PHL_PQR' UNION ALL
SELECT '1234_ABAD_DADADA_XXXX_GHL_PQR'
SELECT t.Data,
udf.ReqData
FROM #Table t
CROSS APPLY [dbo].[Udf_GetReqString] (t.Data) As udf
Result
Data ReqData
-------------------------------------------
1234_XXXX_SHL_PQR SHL
1234_XXXX_RHL_PQR RHL
1234_DDHDFD_XXXX_PHL_PQR PHL
1234_ABAD_DADADA_XXXX_GHL_PQR GHL
DEMO
http://sqlfiddle.com/#!18/2bcfa/1

Related

Function which takes a column values and convert it into a comma separated values

I want an SQL function which takes a result of a select statement as parameter and return a string of comma separated values for the result. If there is a NULL value then it should leave a space and continue with the result.
I tried using the COALESCE() expression, but this takes out the NULL values and returns only valid values.
declare #str varchar(MAX)
SELECT #str= coalesce(#str + ',', '')+ a.D8_BOOK_YEAR_END
FROM (select D8_BOOK_YEAR_END from CUST_PRODUCT_ACCOUNTS
WHERE CUST_PRODUCT_ID=1) a
print #str
For example: In the image, I need to pass the column NAME into the function and it should return me the values as Mango, ,Apple,Grape.
I want an SQL function which takes a result of a select statement as parameter and return a string of comma separated values for the result. If there is a null value then it should leave a space and continue with the result.
You can do that as
--Create a table (just for test)
CREATE TABLE T(
Str VARCHAR(45)
);
INSERT INTO T VALUES
('One'),
(NULL),
('Two'),
('Three');
-- Create a type
CREATE TYPE MyData AS TABLE (Str NVARCHAR(300));
-- Create the function
CREATE FUNCTION dbo.MyFunc(
#Data MyData READONLY
)
RETURNS NVARCHAR(300)
AS
BEGIN
DECLARE #Result NVARCHAR(300) = N'';
SELECT #Result = STUFF(
(
SELECT ',' + ISNULL(Str, '') --Or ' ' as you like
FROM #Data
FOR XML PATH('')
), 1, 1, ''
);
RETURN (#Result);
END
-- Finally, use it
DECLARE #Test MyData;
INSERT INTO #Test SELECT * FROM T;
SELECT dbo.MyFunc(#Test);
Returns:
+------------------+
| (No column name) |
+------------------+
| One,,Two,Three |
+------------------+
And here is a live demo
DECLARE #TABLE TABLE (
ID INT
,Info VARCHAR(MAX)
)
INSERT INTO #TABLE
VALUES (1,'mango')
INSERT INTO #TABLE
VALUES (1,'apple')
INSERT INTO #TABLE
VALUES (1,null)
INSERT INTO #TABLE
VALUES (1,'grape')
INSERT INTO #TABLE
VALUES (2,'mango1')
INSERT INTO #TABLE
VALUES (2,null)
INSERT INTO #TABLE
VALUES (2,null)
INSERT INTO #TABLE
VALUES (2,'grape1')
SELECT ID
,STUFF((
SELECT ',' + isnull(info, '')
FROM #TABLE T1
WHERE T1.id = T2.ID
FOR XML PATH('')
), 1, 1, '')
FROM #TABLE T2
GROUP BY ID
Use ISNULL(field,' ') somewhere to avoid the NULL value behavior.
You can use SQL Concatenation using XML PATH() method as illustrated with following sample query
SELECT
STUFF(
(
SELECT
',' + [user]
FROM dbo.UserCategoryValues
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)'
), 1, 1, ''
) As concatenated_string
This will take care of NULL values. Those NULLs will not be concatenated in to the list
But you can not wrap this code into a function as dynamic SQL that will take the field name and table name and build a SQL statement dynamically. SQL engine will throw an exception in that case

Create Shortcut Function in T-SQL

Using the code below I can create a comma separated list of results from the results of a select statement:
DECLARE #lst NVARCHAR(MAX) = ''
SELECT #lst = COALESCE(NULLIF(#lst,'')+N',',N'') + [colName]
FROM [tableName]
I would like to transform this into a sort of shortcut function, for example:
SELECT dbo.fnColAsList([colName])
FROM [tableName]
Is this at all possible?
Edit:
To give an example, if the select statement returned the following 3 columns:
SELECT [colName] FROM [tableName]
>>> colName
-----------
[1] foo1
[2] foo2
[3] foo3
Then the code would produce a single varchar field:
SELECT #lst = COALESCE(NULLIF(#lst,'')+N',',N'') + [colName]
FROM [tableName];
SELECT #lst
>>> lst
---------
[1] foo1,foo2,foo3
I wish to produce a shortcut so that I can call the code around any column so for example I could do this:
SELECT fnColAsList(colName) AS 'x' FROM [tab1];
>>> x
-----------------
[1] tab1Col1,tab1Col2,tab1Coln
SELECT fnColAsList(colName) AS 'y' FROM [tab2];
>>> y
-----------------
[1] tab2Col1,tab2Col2,tab2Coln
Answered:
CREATE FUNCTION dbo.fnColAsList(#rawxml XML)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN STUFF(((SELECT ',' + AllAttribs.value(N'.','nvarchar(max)')
FROM #rawxml.nodes('/row/#*') x (AllAttribs)
FOR XML PATH(''),ROOT('x'),TYPE).value('/x[1]','VARCHAR(MAX)')
),1,1,'');
END
GO
SELECT dbo.fnColAsList((SELECT [colName] FROM tabName FOR XML RAW))
You can try a trick: Pass in the SELECT as XML by wrapping it in paranthesis. XML is - other than pure SQL - much mightier in dealing with generic names:
CREATE FUNCTION dbo.TestCSV(#SelectForXMLRaw XML)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN STUFF(
(
SELECT ',' + AllAttribs.value(N'.','nvarchar(max)')
FROM #SelectForXMLRaw.nodes('/row/#*') x(AllAttribs)
FOR XML PATH(''),TYPE
).value(N'.','nvarchar(max)'),1,1,''
);
END
GO
DECLARE #tbl TABLE(ID INT IDENTITY,col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100));
INSERT INTO #tbl VALUES ('1a','2a','3a'),('1b','2b','3b');
SELECT ID
,dbo.TestCSV((SELECT * FROM #tbl AS t2 WHERE t2.ID=t.ID FOR XML RAW)) AS Concatenated
FROM #tbl AS t
GO
DROP FUNCTION dbo.TestCSV;
The result
ID Concatenated
1 1,1a,2a,3a
2 2,1b,2b,3b
UPDATE According to your edits
From you edits I take, that you need this for row-wise data. You can achieve this with the same function actually. Just try this
DECLARE #tbl2 TABLE(SomeUnknownColumn VARCHAR(100));
INSERT INTO #tbl2 VALUES ('Val 1'),('Val 2'),('Val 3');
SELECT dbo.TestCSV((SELECT * FROM #tbl2 FOR XML RAW)) AS Concatenated
Thne result:
Val 1,Val 2,Val 3

split string in sql server 2008

In my table, I have Experience field like
Experience
0to0Years
2to0Years
7to12Years
Here I want to split into
Yearfrom - 0
Yearto- 0
How to split strings here. I search so many articles. I can't find the correct solution. Is there any way to do here?
Here is a working solution
declare #yearfrom varchar(2),#yearto varchar(2)
select #yearfrom=substring('0to0Years',0,patindex('%to%','0to0Years')),
#yearto=substring('0to0Years',patindex('%to%','0to0Years')+2,patindex('%Years%','0to0Years')-patindex('%to%','0to0Years')-2)
SqlFiddle: http://www.sqlfiddle.com/#!3/d41d8/12483
For working on your column replace '0to0Years' with column name
declare #yearfrom varchar(2),#yearto varchar(2)
select #yearfrom=substring(col_name,0,patindex('%to%',col_name)),
#yearto=substring(,patindex('%to%',col_name)+2,patindex('%Years%',col_name)-patindex('%to%',col_name)-2)
from table_name where <condition>
Try with following Steps...
--Create Table :
Create Table #Table
(
Name Varchar(50),
Experience Varchar(20)
)
Go
-- Insert Values :
Insert into #Table Values('Tom','0to0Years')
Insert into #Table Values('Victor','0to1Years')
Insert into #Table Values('Mark','11to12Years')
Go
--View Data
Select * from #Table
--Using CharIndex and Substring :
Select Name,
Substring(Experience,1,CharIndex('to',Experience)-1) as Yearfrom,
Substring(Experience,(CharIndex('to',Experience)+2),Len(Replace(Experience,'Years','')) - (CharIndex('to',Experience)+1)) as YearTo
from #Table
Please try:
select 'YearFrom - '+substring(Data, 0, PatIndex('%[to]%', Data)) YearFrom,
'YearTo - '+replace(stuff(Data, 1, PatIndex('%[to]%', Data)+1, ''), 'Years', '') YearTo
from
(
Select '0to0Years' Data union
Select '2to0Years' Data union
Select '756to12Years' Data
)x
Try this using Parsename function
Select parsename(replace(Experience,'to','.'),2) ,
substring(parsename(replace(Experience,'to','.'),1),0,
charindex('Y',parsename(replace(Experience,'to','.'),1)))
from YourTable
Demo in SQLFiddle

Custom aggregate function (concat) in SQL Server

Question: I want to write a custom aggregate function that concatenates string on group by.
So that I can do a
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD1, FIELD2
All I find is SQL CRL aggregate functions, but I need SQL, without CLR.
Edit:1
The query should look like this:
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD0
Edit 2:
It is true that it isn't possible without CLR.
However, the subselect answer by astander can be modified so it doesn't XML-encode special characters.
The subtle change for this is to add this after "FOR XML PATH":
,
TYPE
).value('.[1]', 'nvarchar(MAX)')
Here a few examples
DECLARE #tT table([A] varchar(200), [B] varchar(200));
INSERT INTO #tT VALUES ('T_A', 'C_A');
INSERT INTO #tT VALUES ('T_A', 'C_B');
INSERT INTO #tT VALUES ('T_B', 'C_A');
INSERT INTO #tT VALUES ('T_C', 'C_A');
INSERT INTO #tT VALUES ('T_C', 'C_B');
INSERT INTO #tT VALUES ('T_C', 'C_C');
SELECT
A AS [A]
,
(
STUFF
(
(
SELECT DISTINCT
', ' + tempT.B AS wtf
FROM #tT AS tempT
WHERE (1=1)
--AND tempT.TT_Status = 1
AND tempT.A = myT.A
ORDER BY wtf
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 2, ''
)
) AS [B]
FROM #tT AS myT
GROUP BY A
SELECT
(
SELECT
',äöü<>' + RM_NR AS [text()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
) AS XmlEncodedNoNothing
,
SUBSTRING
(
(
SELECT
',äöü<>' + RM_NR AS [data()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
)
,2
,10000
) AS XmlEncodedSubstring
,
(
STUFF
(
(
SELECT ',äöü<>' + RM_NR + CHAR(10)
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 1, ''
)
) AS XmlDecodedStuffInsteadSubstring
You cannot write custom aggregates outside of the CLR.
The only type of functions you can write in pure T-SQL are scalar and table valued functions.
Compare the pages for CREATE AGGREGATE, which only lists CLR style options, with CREATE FUNCTION, which shows T-SQL and CLR options.
Have a look at something like. This is not an aggregate function. If you wish to implement your own aggregate function, it will have to be CLR...
DECLARE #Table TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO #Table (ID,Val) SELECT 1, 'A'
INSERT INTO #Table (ID,Val) SELECT 1, 'B'
INSERT INTO #Table (ID,Val) SELECT 1, 'C'
INSERT INTO #Table (ID,Val) SELECT 2, 'B'
INSERT INTO #Table (ID,Val) SELECT 2, 'C'
--Concat
SELECT t.ID,
SUM(t.ID),
stuff(
(
select ',' + t1.Val
from #Table t1
where t1.ID = t.ID
order by t1.Val
for xml path('')
),1,1,'') Concats
FROM #Table t
GROUP BY t.ID
Starting from 2017 there is built-in concatenate aggregate function STRING_AGG :)
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017
Found this link around concatenation which covers methods like
Concatenating values when the number of items are not known
Recursive CTE method
The blackbox XML methods
Using Common Language Runtime
Scalar UDF with recursion
Table valued UDF with a WHILE loop
Dynamic SQL
The Cursor approach
Non-reliable approaches
Scalar UDF with t-SQL update extension
Scalar UDF with variable concatenation in SELECT
Though it doesn't cover aggerate functions there may be some use around concatenation in there to help you with your problem.
This solution works with no need of deploy from Visual studio or dll file in server.
Copy-Paste and it Work!
https://github.com/orlando-colamatteo/ms-sql-server-group-concat-sqlclr
dbo.GROUP_CONCAT(VALUE )
dbo.GROUP_CONCAT_D(VALUE ), DELIMITER )
dbo.GROUP_CONCAT_DS(VALUE , DELIMITER , SORT_ORDER )
dbo.GROUP_CONCAT_S(VALUE , SORT_ORDER )
You could do something like what I have done below to create a custom aggregate concatenation function in pure T-SQL. Obviously I have gone with a hard coded table name and group by column but it should illustrate the approach. There is probably some way to make this a truly generic function using dynamic TSQL constructed from input parameters.
/*
User defined function to help perform concatenations as an aggregate function
Based on AdventureWorks2008R2 SalesOrderDetail table
*/
--select * from sales.SalesOrderDetail
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = N'fnConcatenate')
DROP FUNCTION fnConcatenate
GO
CREATE FUNCTION fnConcatenate
(
#GroupByValue int
)
returnS varchar(8000)
as
BEGIN
DECLARE #SqlString varchar(8000)
Declare #TempStore varchar(25)
select #SqlString =''
Declare #MyCursor as Cursor
SET #MyCursor = CURSOR FAST_FORWARD
FOR
Select ProductID
From sales.SalesOrderDetail where SalesOrderID = #GroupByValue
order by SalesOrderDetailID asc
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #TempStore
WHILE ##FETCH_STATUS = 0
BEGIN
select #SqlString = ltrim(rtrim(#TempStore )) +',' + ltrim(rtrim(#SqlString))
FETCH NEXT FROM #MyCursor INTO #TempStore
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
RETURN #SqlString
END
GO
select SalesOrderID, Sum(OrderQty), COUNT(*) as DetailCount , dbo.fnConcatenate(salesOrderID) as ConCatenatedProductList
from sales.SalesOrderDetail
where salesOrderID= 56805
group by SalesOrderID

How to use GROUP BY to concatenate strings in SQL Server?

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