concatinate all rows of a column into single value - sql

I have table called Rule.
RuleId Name
1 A1
2 A2
3 A3
.
.
.
Now I want all the names as single result.
may be like #allnames = A1,A2,A3
Can somebody advise how to write query for this without using loops?
Thanks in advance...

Try this:
SELECT #allnames = STUFF((SELECT distinct ',' + Name
FROM table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SQL Fiddle Demo

DECLARE #names NVARCHAR(MAX)
SELECT #names = coalesce(#names + ',', '') + coalesce(Name, '')
FROM (SELECT distinct Name FROM Rule) x
print #names

Try this one -
DECLARE #temp TABLE ([RuleId] INT, Name CHAR(2))
INSERT INTO #temp([RuleId], Name)
VALUES
(1, 'A1'),
(2, 'A2'),
(3, 'A3')
DECLARE #all_names NVARCHAR(MAX)
SELECT #all_names = STUFF((
SELECT DISTINCT ',' + Name
FROM #temp
--ORDER BY Name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT #all_names
Output -
---------------
A1,A2,A3

Related

many rows into a single column with SQL

I have an one to many table, and if there is rows that have same reference id(Paragraph ID) I want to concatenate so LoginName value have many in same row.
This query does what I want it to do but there is a problem, It replaces first char. the STUFF function requires a replace value.
My question:
How can I do this without replacing first char?
SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N'|' + CAST([LoginName] AS VARCHAR(255))
FROM [dbo].[CM_Signature] f2
WHERE f1.ParagraphID = f2.ParagraphID
FOR XML PATH ('')), 1, 2, '') AS FileNameString
FROM [dbo].[CM_Signature] f1
Expected value:
Daniel | Emma
Here is what you can use:
SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N' | ' + CAST([LoginName] AS VARCHAR(255))
FROM [dbo].[CM_Signature] f2
WHERE f1.ParagraphID = f2.ParagraphID
FOR XML PATH ('')), 1, 1, '') AS FileNameString
FROM [dbo].[CM_Signature] f1
Note the STUFF("...", 1, 1, '') instead of STUFF("...", 1, 2, '').
Because you need to replace 1 char instead of 2 (To remove the first |).
Output:
Daniel|Emma
Also, if you want to have spaces before and after the |, just use this query:
SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N' | ' + CAST([LoginName] AS VARCHAR(255))
FROM [dbo].[CM_Signature] f2
WHERE f1.ParagraphID = f2.ParagraphID
FOR XML PATH ('')), 1, 3, '') AS FileNameString
FROM [dbo].[CM_Signature] f1
Note that this time we removed 3 chars (STUFF("...", 1, 3, '')).
Output:
Daniel | Emma
You were starting your path at position 2 instead of first
SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N'|' + CAST([LoginName] AS VARCHAR(255))
FROM [dbo].[CM_Signature] f2
WHERE f1.ParagraphID = f2.ParagraphID
FOR XML PATH ('')), 1, 1, '') AS FileNameString
FROM [dbo].[CM_Signature] f1 SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N'|' + CAST([name] AS VARCHAR(255))
FROM mytable f2
WHERE f1.paragraphid = f2.paragraphid
FOR XML PATH ('')), 1, 1, '') AS FileNameString
FROM mytable f1
Use this Code:
create table #test (paragraghid int,name VARCHAR(10))
insert into #test values(1929,'Daniel')
insert into #test values(1929,'Emma')
insert into #test values(1935,'Daniel')
select distinct paragraghid,STUFF((select ' | ' + name from #test a
Where a.paragraghid=b.paragraghid for XML PATH('') ),1,2,'') as FilenameString
from #test b
You can write a query as:
DECLARE #CM_Signature table
(
RowId int,
ParagraphID int,
LoginName varchar(10))
Insert into #CM_Signature values
(4,1929,' Daniel'),
(5,1929,' Emma '),
(6,1935,'Daniel')
SELECT DISTINCT
ParagraphID
, STUFF((
SELECT N'| ' + CAST(rtrim(ltrim([LoginName])) AS VARCHAR(255))
FROM #CM_Signature f2
WHERE f1.ParagraphID = f2.ParagraphID
FOR XML PATH ('')), 1, 1, '') AS FileNameString
FROM #CM_Signature f1

How to read value inside a cursor?

There are two tables.
One table contains:
Name value
A 1
B 2
C 3
D 4
another table contains
City value
aa 1
bb 2,3
cc 3
dd 1,2,4
I want an output which contains:
City value Name
aa 1 A
bb 2,3 B,C
cc 3 C
dd 1,2,4 A,B,D
How can i do this using cursor?
Thanks. Your question really made me appreciate normal forms.
Anyhow, I am going to go out on a limb and assume you asked for a cursor-based solution because you assumed the non-normalized data could not be handled.
Once you have the function to materialize the rows into a value list, you can solve this with a simple query.
Given:
CREATE TABLE dbo.NV (Name CHAR(1), Value INT)
CREATE TABLE dbo.CV (City varchar(88), ValueList VARCHAR(88))
loaded with the data you indicated.
And this SQL script:
GO
CREATE FUNCTION dbo.f_NVList(#VList VARCHAR(MAX)) RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #VAL VARCHAR(928)='',
#FIDescr VARCHAR(55)
SELECT #VAL = COALESCE(#VAL + LTRIM(map.name),'') + ','
FROM dbo.nv Map
WHERE CHARINDEX(','+LTRIM(STR(map.value)) + ',', ','+#VList + ',' ) > 0
SET #VAL = SUBSTRING(#VAL,1,len(#VAL)-1)
RETURN(#VAL)
END
GO -- end of function
-- this generates the output, using the function to materialize the name-values
SELECT cv.* , dbo.f_NVList(cv.ValueList ) as NameList FROM dbo.CV cv;
producing your output:
PLEASE DON'T - but If you really need the cursor for some reason, instead of
SELECT cv.* , dbo.f_NVList(cv.ValueList ) as NameList FROM dbo.CV cv;
use this
OPEN BadIdea;
FETCH NEXT FROM BadIdea INTO #C, #VList
WHILE ##FETCH_STATUS = 0
BEGIN
SET #NameList = dbo.f_NVList(#Vlist)
INSERT INTO #OUT VALUES( #C, #VLIST , #NameList )
FETCH NEXT FROM BadIdea INTO #C, #VList
END
CLOSE BadIdea
DEALLOCATE BadIdea
select * from #OUT ;
Please give a try on this:
;with nv as (
select *
from (values ('A', '1'), ('B', '2'), ('C', '3'), ('D', '4')) a (Name, value))
, cv as (
select *
from (values ('aa', '1'), ('bb', '2,3'), ('cc', '3'), ('dd', '1,2,4')) a(City, value)
)
, cv2 as (
select cv.City
, case when charindex(',',cv.value)>0 then LEFT(cv.value, charindex(',',cv.value)-1) else cv.value end value
, case when charindex(',',cv.value)>0 then right(cv.value, LEN(cv.value)-len(LEFT(cv.value, charindex(',',cv.value)-1)+',')) end leftover
from cv
union all
select cv.City
, case when charindex(',',cv.leftover)>0 then LEFT(cv.leftover, charindex(',',cv.leftover)-1) else cv.leftover end value
, case when charindex(',',cv.leftover)>0 then right(cv.leftover, LEN(cv.leftover)-len(LEFT(cv.leftover, charindex(',',cv.leftover)-1)+',')) end leftover
from cv2 cv
where cv.leftover is not null
)
select *
, stuff((
select ','+nv.Name
from cv2
join nv on nv.value=cv2.value
where cv2.City=cv.City
for xml path('')
), 1, 1, '') Name
from cv
With cv2 I split the values to City, with a recursive CTE. After that I calculate the new Name for each City.
I don't know how fast is on a big table, but I think it is better then cursor.
using CROSS APPLY we will initially delimit all the values and then we can acheieve using XML path () and CTE's
DECLARE #Name table (name varchar(5),value int)
INSERT INTO #Name (name,value)values ('A',1),('B',2),('C',3),('D',4)
DECLARE #City table (city varchar(10),value varchar(10))
INSERT INTO #City (city,value)values ('aa','1'),('bb','2,3'),('cc','3'),('dd','1,2,4')
Code :
;with CTE AS (
SELECT A.city,
Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT city,
CAST ('<M>' + REPLACE(value, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM #City
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
),CTE2 AS (
Select c.city,t.value,STUFF((SELECT ', ' + CAST(name AS VARCHAR(10)) [text()]
FROM #Name
WHERE value = c.Data
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
from CTE C
INNER JOIN #Name t
ON c.Data = t.value
)
select DISTINCT c.city,STUFF((SELECT ', ' + CAST(value AS VARCHAR(10)) [text()]
FROM CTE2
WHERE city = C.city
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') As Value ,STUFF((SELECT ', ' + CAST(List_Output AS VARCHAR(10)) [text()]
FROM CTE2
WHERE city = C.city
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ')As Name from CTE2 C
First, you need a function to split your comma-delimited values. Here is the DelimitedSplit8K written by Jeff Moden and improved by the community. This is regarded as one of the fastest SQL-based string splitter.
You should also read on FOR XML PATH(''), a method to concatenate strings. Check this article by Aaron Bertrand for more information.
SELECT
*
FROM Table2 t2
CROSS APPLY(
SELECT STUFF((
SELECT ',' + Name
FROM Table1
WHERE Value IN(
SELECT CAST(s.Item AS INT) FROM dbo.DelimitedSplit8K(t2.Value, ',') s
)
FOR XML PATH(''), type).value('.', 'VARCHAR(MAX)'
), 1, 1, '')
)x(Name)
SQL Fiddle
Notes:
Make sure to get the latest version of the DelimitedSplit8K.
For other splitter functions, check out this article by Aaron Bertrand.

SQL Declare Query only returns 1 row result

I'm trying to get a result from our db with one identifier (ArtNr) and one big chunk of text that also contains returns (Edit),
I also want the text from the (Edit) column to be stringed?
(like this: "texttexttext" it's the "" I need in the beginning and end of the text)
I have come up with the query below but it only gives 1 row in reply (should be like 4000) and it does of course not give me the Edit captioned with "" since I have no clue how to do it :)
Thanks in advance! /Christian
USE MSPes2t
DECLARE #result nvarchar(max) , #test nvarchar(255)
SELECT #result = AR.Edit , #test = AR.ArtNr
FROM DBO.AR
WHERE AR.ArtNr = '%'
ORDER BY AR.ArtNr
SELECT #test AS artnr,
#result AS edit
This is the result im searching for:
Like this:
ArtNr | Edit
------+----------------------------------------
12001 | "edit"
12002 | "edit"
28001 | "edit"
The following query should give you the desired results (although I am not exactly sure how you want to delimit your edits, I have used a semi-colon):
SELECT ArtNr = '12001',
Edit = STUFF(( SELECT ';"' + ar.Edit + '"'
FROM dbo.AR
WHERE AR.ArtNr = '12001'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
A full explanation of how using the xml extension FOR XML PATH() works to concanate rows into a single field can be found in this answer
If you need to get the edits concatenated for more than one ArtNo then you could use somethig like:
SELECT ar.ArtNr,
Edit = STUFF(( SELECT ';"' + ar2.Edit + '"'
FROM dbo.AR AS ar2
WHERE AR2.ArtNr = ar.ArtNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM dbo.Ar
WHERE ar.ArtNr IN ('12001', '12002')
GROUP BY ar.ArtNr;
SIMPLE WORKING EXAMPLE
DECLARE #AR TABLE (ArtNr VARCHAR(5), Edit NVARCHAR(MAX));
INSERT #AR (ArtNr, Edit)
VALUES ('12001', 'editeditedit'), ('12001', 'edit2 edit2'),
('12001', 'edit3'), ('12002', 'edit2 edit2');
SELECT ar.ArtNr,
Edit = STUFF(( SELECT ';"' + ar2.Edit + '"'
FROM #AR AS ar2
WHERE AR2.ArtNr = ar.ArtNr
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #AR AS ar
WHERE ar.ArtNr IN ('12001', '12002')
GROUP BY ar.ArtNr;
Gives:
ArtNr | Edit
------+----------------------------------------
12001 | "editeditedit";"edit2 edit2";"edit3"
12002 | "editeditedit"
EDIT
Based on your required output I think your query is as simple as concatenating " to either end of your edit field:
SELECT AR.ArtNr,
'"' + AR.edit + '"' AS Edit
FROM DBO.AR
WHERE AR.ArtNr = '%'
ORDER BY ArtNr;
In SQL Server 2012 and later you can use CONCAT but it doesn't offer much advantage in this scenario.

Have all Records in one Field

how is possible to have all records of one field into one field
Id, No , FDevice
1 , 1 , 'A'
2 , 1 , 'B'
3 , 1 , 'C'
4 , 2 , 'D'
5 , 2 , 'E'
I want to have
No , FDevice
1 , A-B-C
2 , D-E
Thank you for your help
use STUFF() - which inserts a string into another string.
SELECT
[No],
STUFF(
(SELECT '-' + [FDevice]
FROM TableName
WHERE [No] = a.[No]
FOR XML PATH (''))
, 1, 1, '') AS FDevice
FROM TableName AS a
GROUP BY [No]
SQLFiddle Demo
There're a well-known solution for aggregate concatenation in SQL Server, using select ... for xml path(''), but I have to say that many people using it incorrectly. Correct way to do this would be
select
a.[No],
stuff(
(
select '-' + t.[FDevice]
from TableName as t
where t.[No] = a.[No]
for xml path(''), type
).value('.', 'nvarchar(max)')
, 1, 1, '') as FDevice
from (select distinct [No] from TableName) as a;
sql fiddle demo
The main part is to use xml type inside the query and then to convert it into varchar using value function, otherwise you can end up with incorrectly converted special chars like '>', '<', '&' and so on. SQLfiddle somehow doesn't show the difference, but here's a script which can show you what can happen if you don't use xml type:
declare #TableName table
([Id] int, [No] int, [FDevice] varchar(3))
;
INSERT INTO #TableName
([Id], [No], [FDevice])
VALUES
(1, 1, 'A<'),
(2, 1, 'B'),
(3, 1, '&C'),
(4, 2, 'D'),
(5, 2, 'E')
;
SELECT
[No],
STUFF(
(SELECT '-' + [FDevice]
FROM #TableName
WHERE [No] = a.[No]
FOR XML PATH (''))
, 1, 1, '') AS FDevice
FROM #TableName AS a
GROUP BY [No];
outputs
No FDevice
--------------------
1 A<-B-&C
2 D-E
select
a.[No],
stuff(
(
select '-' + t.[FDevice]
from #TableName as t
where t.[No] = a.[No]
for xml path(''), type
).value('.', 'nvarchar(max)')
, 1, 1, '') as FDevice
from (select distinct [No] from #TableName) as a;
outputs
No FDevice
--------------------
1 A<-B-&C
2 D-E

SQL query to split a column based on hardcoded values

I have a table eg assume this setup
table MyTable has various columns Id, UserId, col1, col2 col3 including column called Stuff.
I want to output certain Columns from MyTable with a query
but i want to split the 'Stuff' column such that 2 new columns are shown in the query
I can define the categories hardcoded, im not sure how this can be represented in sql
Categoy1 = "alpha, bravo, delta, gamma';
Categoy2 = "charlie, echo, hotel';
MyTable
ID | UserID | Stuff | Other Cols....
----------------------------------------------------------
1 1 alpha
2 2 hotel
3 1 charlie
4 1 echo
5 1 gamma
6 2 bravo
7 2 delta
i want the select query to show
UserId | Category1 | Catergory2
----------------------------------------------------------
1 alpha, gamma charlie, echo
---------------------------------------------------------
2 bravo, delta hotel
----------------------------------------------------------
i.e produce 2 columns split based on whether the stuff column contains an item from category1 or category2
based on a distinct userId the categories content can be comma separated as hown above
Please can you show how this can be done
Hope this makes sense.
Thanks
You can use the xml extensions to concatenate your strings, then just hard code the categories into each subquery:
CREATE TABLE #T (ID INT, UserID INT, [Stuff] VARCHAR(300))
INSERT #T VALUES
(1, 1, 'alpha'),
(2, 2, 'hotel'),
(3, 1, 'charlie'),
(4, 1, 'echo'),
(5, 1, 'gamma'),
(6, 2, 'bravo'),
(7, 2, 'delta');
SELECT UserID,
[Category1] = STUFF(( SELECT ', ' + [Stuff]
FROM #T t2
WHERE [Stuff] IN ('alpha', 'bravo', 'delta', 'gamma')
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ''),
[Category2] = STUFF(( SELECT ', ' + [Stuff]
FROM #T t2
WHERE [Stuff] IN ('charlie', 'echo', 'hotel')
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
FROM ( SELECT DISTINCT UserID
FROM #T
) t
Example on SQL Fiddle
You could define your categories at the start in a CTE (Categories) for improved readibility:
WITH Categories AS
( SELECT Category, Name
FROM (VALUES
(1, 'alpha'),
(1, 'bravo'),
(1, 'delta'),
(1, 'gamma'),
(2, 'charlie'),
(2, 'echo'),
(2, 'hotel')
) t (Category, Name)
), Data AS
( SELECT UserID, [Stuff], Category
FROM T
INNER JOIN Categories c
ON c.Name = T.[Stuff]
)
SELECT UserID,
[Category1] = STUFF(( SELECT ', ' + [Stuff]
FROM Data t2
WHERE Category = 1
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ''),
[Category2] = STUFF(( SELECT ', ' + [Stuff]
FROM Data t2
WHERE Category = 2
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
FROM ( SELECT DISTINCT UserID
FROM T
) t
Example on SQL Fiddle
My try, the technique I learned from Stack Overflow!... Please check:
DECLARE #Categoy1 NVARCHAR(MAX) = 'alpha, bravo, delta, gamma',
#Categoy2 NVARCHAR(MAX) = 'charlie, echo, hotel'
SELECT
UserID,
STUFF((SELECT ', ' + display_term
FROM sys.dm_fts_parser('"'+ ',' + #Categoy1 + '"', 1033, NULL, 0) INNER JOIN
YourTable T on display_term=[Stuff]
WHERE T.UserID= x.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') Category1,
STUFF((SELECT ', ' + display_term
FROM sys.dm_fts_parser('"'+ ',' + #Categoy2 + '"', 1033, NULL, 0) INNER JOIN
YourTable T on display_term=[Stuff]
WHERE T.UserID= x.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') Category2
FROM YourTable x
GROUP BY UserID