using recursive function to simulate group_concat - sql

I have a table like
ID NAME
1 STALIN
2 MERWIN
1 AMALA
1 RAYON
i want the out as
ID NAME
1 STALIN,AMALA,RAYON
2 MERWIN
How can do this using recursive function

You can do this using XML Path
SELECT ID, NAME =
STUFF((SELECT ', ' + NAME
FROM your_table b
WHERE b.ID = a.ID
FOR XML PATH('')), 1, 2, '')
FROM your_table a
GROUP BY ID
Live Demo
Follow this link and see the case 3. it explains why to use XML path over recursive cursor.

You can use FOR XML PATH to get the result:
for example:
DECLARE #EXAMPLE TABLE (ID int, NAME nvarchar(10))
INSERT #EXAMPLE VALUES (1,'STALIN'),(2,'MERWIN'),(1,'AMALA'),(1,'RAYON')
SELECT
ID,
SUBSTRING(NAME,0,LEN(NAME)) AS NAME
FROM ( SELECT DISTINCT
T1.ID,
STUFF(( SELECT DISTINCT '' + t2.NAME + ','
FROM #EXAMPLE T2
WHERE T1.ID = T2.ID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,0,'') NAME
FROM #EXAMPLE t1) AS info

Related

Progress 12.2 - Concat rows with same ID

I use the database Progress 12.2 and I want to group rows with same id
For example I have
ID Code
1 PB
1 RO
And I want :
ID Code
1 PB, RO
This is my request :
SELECT id, code FROM table WHERE table.id = 1
I tried String_agg, Group_concat ... but nothing works. Anyone has an idea ?
Regards,
You can use someting like that;
DECLARE #TBL TABLE (ID Int, Code nvarchar(5))
INSERT INTO #TBL values (1 ,'PB'),(1 ,'RO')
SELECT DISTINCT
T1.ID,
CONCAT_STRING = STUFF((
SELECT ',' + T2.Code
FROM #TBL T2
WHERE T2.ID = T1.ID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #TBL T1
The result will be like this;

Delete ; symbol at string end (SQL)

I have SQL script that gets roles and return string with list of roles
Here is part of code
, ISNULL(SUBSTRING(
(
SELECT ECBR.RoleName + ';' AS [text()]
FROM [External].[vw_ExecutiveChangeByRole] ECBR
WHERE ECBR.VendorExecutiveChangeId = ECH.VendorExecutiveChangeId
AND (ECBR.IsCurrentOrPrior IS NULL OR ECBR.IsCurrentOrPrior = 1)
AND ECBR.RecognizedWith = 'ML'
ORDER BY ECBR.RoleId
FOR XML PATH ('')
), 1, 1000), '') AS RecognizedCurrentRoles
And now showing like VP;Controller;
How I can make this string without last;, just VP;Controller
It depends on your version of SQL Server.
If you have a version 2017 or higher than this is very easy, see this example
let's first build some sample data
declare #t table (groupid int, name varchar(50))
insert into #t (GroupID, name)
values (1, 'VP'), (1, 'Controller'), (2,'A'), (2,NULL), (2,'whatever');
the query for sql server 2017 or higher
select t.groupid,
string_agg(t.name, ';') as name
from #t t
group by t.groupid
the result is
groupid name
------- ----
1 VP;Controller
2 A;whatever
On older versions you need to select for XLM Path and then use STUFF to get rid of the extra ;
select t.groupid,
( select ';' + t2.name
from #t t2
where t2.groupid = t.groupid
for XML PATH('')
) as without_stuff,
stuff( ( select ';' + t2.name
from #t t2
where t2.groupid = t.groupid
for XML PATH('')
),
1, 1, '') as with_stuff
from #t t
group by t.groupid
the result is
groupid without_stuff with_stuff
------- ------------- ----------
1 ;VP;Controller VP;Controller
2 ;A;whatever A;whatever
EDIT
As commented by #Charlieface the XLM PATH could return unexpected results when the character is a special XML character, for example <
lets look at this example
select t.groupid,
( select '<' + t2.name
from #t t2
where t2.groupid = t.groupid
for XML PATH('')
) as without_stuff
from #t t
group by t.groupid
it will return this result
groupid without_stuff
------- -------------
1 <VP<Controller
2 <A<whatever
I can imagine that this is not what you expected.
To solve this, alter the query like this
select t.groupid,
(select '<' + t2.name
from #t t2
where t2.groupid = t.groupid
for XML PATH(''), TYPE
).value('text()[1]','nvarchar(max)') as without_stuff
from #t t
group by t.groupid
Now the result will be more what you want
groupid without_stuff
------- -------------
1 <vp<Controller
2 <A<whatever

How to join rows in some table in SQL Server?

i want join some table then concatenation columns
MyTable
-------------------------------
ID RowId LangId Caption
-------------------------------
1 1 1 ڕۆشتن
2 1 2 Go
3 1 3 اذهب
4 2 1 ئاو
5 2 2 water
6 2 3 ماء
I want join concatenation Caption column ex: for RowId 1 'ڕۆشتن - Go - اذهب'
Desired output
--------------------
RowId Caption
--------------------
1 ڕۆشتن - Go - اذهب
2 ئاو- water - ماء
I seen link but can't help me
You can use string_agg():
select rowid,
string_agg(caption, ' ') within group (order by langid) as caption
from t
group by rowid;
You can use for xml for older vresion :
select r.rowid,
stuff( (select ' - '+t.caption
from table t
where t.rowid = r.rowid
order by t.LangId
for xml path('')
), 1, 1, ''
) as Caption
from (select distinct rowid from table ) r;
You can use string_agg() for newer version SQL Server 17+ :
select t.rowid,
string_agg(t.caption, ' ') within group (order by t.langid) as caption
from table t
group by t.rowid;
You can try the following query using for xml as shown below.
SELECT DISTINCT t.RowId,
STUFF((SELECT distinct ' - ' + t1.[Caption]
FROM
(
SELECT t.[RowId],
t2.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS t2 ON t2.[RowId] = t.[RowId]
)
t1
WHERE t.RowId = t1.RowId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') CaptionList
FROM
(
SELECT t.[RowId],
por.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS por ON por.RowId = t.RowId
)t;
Here is the live db<>fiddle demo.
Try this:
SELECT DISTINCT RowID,
STUFF((SELECT DISTINCT Sub.caption +' - '
FROM #tab Sub
WHERE Sub.rowid = Main.rowid
FOR XML PATH('')), 1, 1, '') AS Caption
FROM #tab Main
I think this Query can perfectly fit for your case.

SQL Query using inner join

CategoryTable
Code Name
1 Food
2 Non-Food
Existing Table Consists list of category, as for example, I have two only Food and Non-Food
As challenge, I am assigning tenants with category or categories (multiple assignment, as there are tenants which are categorized as food and non-food). I i used to insert Tenant and Code to a new table creating this output
TenantAssignTable
Tenant Code
Tenant1 1,2
Tenant2 1
What I need to do, is to load the TenantAssingTable to gridview consisting the Name of the CategoryCode too like this
Desired Output
Tenant CCode Name
Tenant1 1,2 Food,Non-Food
Tenant2 1 Food
I used inner join in my code, but this is limited as I have a string of combined code in Code column.
Select a.tenant, a.ccode, b.name
from TenantAssignTable a inner join CategoryTable b
on a.CCode = b.code
Is there anyway to achieve this kind of output? I know that this is unusual in SQL coding but this is what is challenge as what the desired output is concerned and needs which is to have a multiple assignment of category to a single tenant.
Thanks in advance!
Think simple;
You can with LIKE and XML PATH
DECLARE #CategoryTable TABLE (Code VARCHAR(50), Name VARCHAR(50))
INSERT INTO #CategoryTable
VALUES
('1', 'Food'),
('2', 'Non-Food')
DECLARE #TenantAssignTable TABLE (Tenant VARCHAR(50), Code VARCHAR(50))
INSERT INTO #TenantAssignTable
VALUES
('Tenant1', '1,2'),
('Tenant2', '1')
SELECT
T.Tenant ,
T.Code,
STUFF(
(SELECT
',' + C.Name
FROM
#CategoryTable C
WHERE
',' + REPLACE(T.Code, ' ', '') + ',' LIKE '%,' + C.Code + ',%'
FOR XML PATH('')
), 1, 1, '') A
FROM
#TenantAssignTable T
Result:
Tenant Code A
--------------- ------------ ---------------
Tenant1 1,2 Food,Non-Food
Tenant2 1 Food
You can use some XML transformations:
DECLARE #x xml
SELECT #x = (
SELECT CAST('<t name="'+a.tenant +'"><a>'+REPLACE(a.code,',','</a><a>') +'</a></t>' as xml)
FROM TenantAssignTable a
FOR XML PATH('')
)
;WITH cte AS (
SELECT t.v.value('../#name','nvarchar(max)') as Tenant,
t.v.value('.','int') as CCode,
ct.Name
FROM #x.nodes('/t/a') as t(v)
INNER JOIN CategoryTable ct
ON ct.Code = t.v.value('.','int')
)
SELECT DISTINCT
c.Tenant,
STUFF((SELECT ','+CAST(CCode as nvarchar(10))
FROM cte
WHERE c.Tenant = Tenant
FOR XML PATH('')
),1,1,'') as CCode,
STUFF((SELECT ','+Name
FROM cte
WHERE c.Tenant = Tenant
FOR XML PATH('')
),1,1,'') as Name
FROM cte c
Output:
Tenant CCode Name
Tenant1 1,2 Food,Non-Food
Tenant2 1 Food
The first part (defining #x variable) will bring your table to this kind of XML:
<t name="Tenant1">
<a>1</a>
<a>2</a>
</t>
<t name="Tenant2">
<a>1</a>
</t>
Then in CTE part we join XML with table of categories. And after all get data from CTE with the help of FOR XML PATH.
Create Function as below which return Table from separated Value
CREATE FUNCTION [dbo].[fnSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
Create Function as below which return comma separated Name
CREATE FUNCTION [dbo].[GetCommaSeperatedCategory]
(
#Codes VARCHAR(50)
)
RETURNS VARCHAR(5000)
AS
BEGIN
-- Declare the return variable here
DECLARE #Categories VARCHAR(5000)
SELECT #Categories= STUFF
(
(SELECT ',' + convert(varchar(10), Name, 120)
FROM Category
WHERE Code IN (SELECT Id FROM [dbo].[fnSplit] (#Codes,',') )
ORDER BY Code
FOR XML PATH (''))
, 1, 1, '')
RETURN #Categories
END
AND Last:
SELECT
Tenant,
Code,
(SELECT [dbo].[GetCommaSeperatedCategory] (Code)) AS Name
FROM TblTenant

Duplicates without using While or Cursor in T-SQL

ID Name
1 A
1 B
1 C
2 X
2 Y
3 P
3 Q
3 R
These are the columns in a table. I want to get output like
ID Company
1 A,B,C
2 X, Y
3 P,Q,R
Restriction is that I cannot use WHILE or CURSOR. Please write a query for the same.
This query should do it - uses FOR XML PATH which is new in SQL Server 2005 - hope you are on 2005 or higher, you didn't clearly specify.....
SELECT
ID,
STUFF(CAST((SELECT ','+Name FROM dbo.YourTable t2
WHERE t2.ID = dbo.YourTable.ID
FOR XML PATH(''), TYPE) AS VARCHAR(MAX)), 1, 1, '') AS 'Company'
FROM
dbo.YourTable
GROUP BY
ID
Here's a solution using the CROSS APPLY method:
select id, sub.names
from (
select distinct id from YourTable
) a
cross apply (
select name + ', ' as [text()]
from YourTable b
where b.id = a.id
for xml path('')
) sub(names)
For 2005 version:
CREATE TABLE dbo.TEST([Type] INTEGER, [Name] NVARCHAR(100), [Qty] INTEGER)
GO
INSERT dbo.TEST VALUES(1, N'a', 5)
INSERT dbo.TEST VALUES(1, N'b', 6)
INSERT dbo.TEST VALUES(2, N'c', 44)
INSERT dbo.TEST VALUES(3, N'd', 1)
GO
select [Type],
[Description] = replace((select [Name] + ':' + cast([Qty] as varchar) as 'data()'
from TEST where [Type] = t.[Type] for xml path('')), ' ', ',')
from dbo.TEST t
group by [Type]
go
drop table dbo.TEST
You can group on the ID to get the unique values, then get the comma separated string for each using a for xml query:
select
a.ID,
substring((
select ', ' + Name
from Test1
where Test1.ID = a.ID
for xml path('')
), 3, 1000) as Company
from
TheTable a
group by
a.ID