Let's suppose I have a temporary table which looks like this:
+----+------+
| Id | Value|
+----+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----+------+
And I want my table to be like this:
+----+----------+
| Id | ValueList|
+----+----------+
| 1 | 1,2,3 |
| 2 | 1,2 |
+----+----------+
So basically I need to group my values as a comma separated list.
I already tried the following:
SELECT Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable
GROUP BY Id
But I get something like:
+----+---------------------+
| Id | ValueList |
+----+---------------------+
| 1 | 1,1,1,1,1,1,... |
+----+---------------------+
I cant find what I am doing wrong. Could someone help with this query? Or point me to a right direction?
Thank you.
You are missing the condition inside the sub query.
SELECT t2.Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t2
GROUP BY t2.Id
Demo
One alternative to using GROUP BY on the Id would be to use select distinct:
SELECT DISTINCT
Id,
STUFF((SELECT ',' + CAST(t2.VALUE AS varchar)
FROM #MyTable t2
WHERE t2.Id = t1.Id
FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t1
Demo
One can also combine a FOR XML with a CROSS APPLY (or an OUTER APPLY) for this.
Example snippet:
declare #T table (id int, value int);
insert into #T values (1,1),(1,2),(1,3),(2,1),(2,2);
select id, stuff(x.list,1,1,'') as list
from (select distinct id from #T) as t
cross apply (
select concat(',',t2.value)
from #T t2
where t2.id = t.id
for xml path('')
) x(list)
order by id;
Result:
id list
-- -----
1 1,2,3
2 1,2
And starting from MS Sql Server 2017, STRING_AGG can be used instead.
select id, string_agg(value,',') as list
from Yourtable t
group by id;
Try this :
create table #t(id int, value int)
insert into #t values
(1,1),
(1,2),
(1,3),
(2,1),
(2,2)
SELECT t2.Id,
STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #t t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS list
FROM #t t2
GROUP BY t2.Id
output :
Id list
--- -------
1 1,2,3
2 1,2
Simple Solution
SELECT Id, GROUP_CONCAT(Value) as ValueList FROM MyTable GROUP BY Id;
add distinct to values if required
SELECT Id, GROUP_CONCAT(DISTINCT Value) as ValueList FROM MyTable GROUP BY Id;
Related
Let's suppose I have a temporary table which looks like this:
+----+------+
| Id | Value|
+----+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----+------+
And I want my table to be like this:
+----+----------+
| Id | ValueList|
+----+----------+
| 1 | 1,2,3 |
| 2 | 1,2 |
+----+----------+
So basically I need to group my values as a comma separated list.
I already tried the following:
SELECT Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable
GROUP BY Id
But I get something like:
+----+---------------------+
| Id | ValueList |
+----+---------------------+
| 1 | 1,1,1,1,1,1,... |
+----+---------------------+
I cant find what I am doing wrong. Could someone help with this query? Or point me to a right direction?
Thank you.
You are missing the condition inside the sub query.
SELECT t2.Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t2
GROUP BY t2.Id
Demo
One alternative to using GROUP BY on the Id would be to use select distinct:
SELECT DISTINCT
Id,
STUFF((SELECT ',' + CAST(t2.VALUE AS varchar)
FROM #MyTable t2
WHERE t2.Id = t1.Id
FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t1
Demo
One can also combine a FOR XML with a CROSS APPLY (or an OUTER APPLY) for this.
Example snippet:
declare #T table (id int, value int);
insert into #T values (1,1),(1,2),(1,3),(2,1),(2,2);
select id, stuff(x.list,1,1,'') as list
from (select distinct id from #T) as t
cross apply (
select concat(',',t2.value)
from #T t2
where t2.id = t.id
for xml path('')
) x(list)
order by id;
Result:
id list
-- -----
1 1,2,3
2 1,2
And starting from MS Sql Server 2017, STRING_AGG can be used instead.
select id, string_agg(value,',') as list
from Yourtable t
group by id;
Try this :
create table #t(id int, value int)
insert into #t values
(1,1),
(1,2),
(1,3),
(2,1),
(2,2)
SELECT t2.Id,
STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #t t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS list
FROM #t t2
GROUP BY t2.Id
output :
Id list
--- -------
1 1,2,3
2 1,2
Simple Solution
SELECT Id, GROUP_CONCAT(Value) as ValueList FROM MyTable GROUP BY Id;
add distinct to values if required
SELECT Id, GROUP_CONCAT(DISTINCT Value) as ValueList FROM MyTable GROUP BY Id;
I'm new to database concepts. So, i need some help in solving a particular problem.
Say i have a table named emp whose data are as below:
id | dept | doj
100 | FS | 02-04-13
100 | HST | 02-04-14
100 | ETA | 02-04-15
What i want to display is:
id | from | to | doj
100 | FS | HST | 02-04-14
100 | HST | ETA | 02-04-15
Any help would be appreciated. Thank you.
The database i'm using is MS-SQL server 13v.
You can use apply :
select t.id, t.dept as [from], t1.dept as [to], t1.doj
from table t cross apply
(select top (1) t1.*
from table t1
where t1.id = t.id and t1.doj > t.doj
order by t1.doj
) t1
With a self join to the table:
select t.id, tt.dept [from], t.dept [to], t.doj
from tablename t inner join tablename tt
on tt.id = t.id and
tt.doj = (select max(doj) from tablename where id = t.id and doj < t.doj)
See the demo
If I understand your question correctly and you want to get data from current and subsequent row in the same result set, you may use LEAD() function:
Input:
CREATE TABLE #Table (
id int,
dept varchar(10),
doj date
)
INSERT INTO #Table
(id, dept, doj)
VALUES
(100, 'FS', '2013-04-02'),
(100, 'HST', '2014-04-02'),
(100, 'ETA', '2015-04-02'),
(101, 'XTA', '2015-04-02'),
(101, 'YTA', '2015-04-02')
Statement:
SELECT *
FROM
(
SELECT
id,
dept [from],
LEAD(dept) OVER (PARTITION BY id ORDER BY id, doj) AS [to],
LEAD(doj) OVER (PARTITION BY id ORDER BY id, doj) AS [doj]
FROM #Table
) t
WHERE t.[to] IS NOT NULL
Output:
id from to doj
100 FS HST 2014-04-02
100 HST ETA 2015-04-02
101 XTA YTA 2015-04-02
I have 2 connected tables: Producto and Productos_ProductosRelacionados
Producto Productos_ProductosRelacionados
|id|referencia| |id|idProducto|idProductoRelacionado|
|1 | A | |1 | 1 | 2 |
|2 | B | |2 | 1 | 3 |
|3 | C | |3 | 3 | 4 |
|4 | D | |4 | 3 | 5 |
|5 | E |
I need this:
|idProducto|referencia|
| 1 | B,C |
| 2 | |
| 3 | D,E |
I have older SQL server so can not use STRING_AGG.
So far I only achieve concatenating idProductoRelacionado:
|idProducto|idProductoRelacionado|
| 1 | 2,3 |
with:
SELECT pr1.idProducto
,STUFF((
SELECT ',' + CONVERT(varchar, pr.idProductoRelacionado)
FROM [Productos_ProductosRelacionados] as pr
WHERE pr.idProducto = pr1.idProducto
FOR XML PATH('')), 1, 1, '') as RelacionadosID
FROM [dbo].[Producto] as p1
join [Productos_ProductosRelacionados] as pr1 on p1.id = pr1.idProductoRelacionado
GROUP BY pr1.idProducto
If I try the same approach to concatenate referencia column it gives me: "Column 'dbo.Producto.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
select pr1.idProducto
,STUFF((
SELECT ',' + p.referencia
FROM [dbo].[Producto] as p
WHERE p.id = p1.id
FOR XML PATH('')), 1, 1, '') as RelacionadosREF
from [dbo].[Producto] as p1
join [Productos_ProductosRelacionados] as pr1 on p1.id = pr1.idProductoRelacionado
GROUP BY pr1.idProducto
I do not understand the difference between 2 queries, why first one is working and second is not.
Simple Join should work. Check SQLFiddle
SELECT
id,
(SELECT
cast (p1.referencia as varchar(100)) + ','
FROM producto p
LEFT JOIN Productos_ProductosRelacionados pr
on pr.idProduct = p.id
LEFT JOIN producto p1
on p1.id = pr.idProductoRelacionado
WHERE p.id = src.id
FOR XML PATH('')) as referencia
FROM
producto src;
I ran into this a while back. You have to add a join to your stuff statement:
create table #prod (
id int,
ref varchar(1)
)
insert into #prod values
(1,'A'),
(2,'B'),
(3,'C'),
(4,'D'),
(5,'E')
create table #prod_rel (
id int,
pid int,
id_rel int
)
insert into #prod_rel values
(1,1,2),
(2,1,3),
(3,3,4),
(4,3,5)
select distinct
pid
,STUFF(
(SELECT ',' + c.ref FROM #prod_rel b inner join #prod c on c.id = b.id_rel where b.pid = a.pid FOR XML PATH ('')), 1, 1, ''
)
from #prod_rel a
drop table #prod
,#prod_rel
You can use recursive query method, like this:
--Create tables for example
select * into #Producto from (
select 1 id,'A' referential union all select 2,'B' union all select 3,'C' union all select 4,'D' union all select 5,'E'
) tmp;
select * into #Productos_ProductosRelacionados from (
select 1 id,1 idProducto,2 idProductoRelacionado union all select 2,1,3 union all select 3,3,4 union all select 4,3,5
) tmp;
-- Recurse query
With tmp as (
select ROW_NUMBER() over(partition by f1.ID order by f1.id, f3.referential desc) RangID,
count(*) over(partition by f1.ID order by f1.id) NbID,
f1.id, f3.referential
from #Producto f1
left outer join #Productos_ProductosRelacionados f2 on f1.id=f2.idProducto
left outer join #Producto f3 on f2.idProductoRelacionado=f3.id
),
Recurse as (
select f1.id, f1.RangID, f1.NbID, cast(f1.referential as varchar(2000)) referential, 1 rangrecur from tmp f1 where RangID=1
union all
select f1.id, f1.RangID, f1.NbID, cast(isnull(f1.referential, '') + ',' + isnull(f2.referential, '') as varchar(2000)) referential, f2.rangrecur + 1 as rangrecur
from tmp f1 inner join Recurse f2 on f1.id=f2.id and f1.RangID-1=f2.RangID
)
select ID, Referential from recurse
where NbID=rangrecur
order by ID;
I need a little help getting the results I need. I have 2 tables with a common field of id. I want to combine the values from table B that have the same id
Table A
id | name | somevalue1
1 | dud | 12345
2 | duda | 8908
Table B
id | somevalue2
1 | 56545
2 | 545665
1 | 89875
2 | 12524
Desired Result
id | somevalue2 combined
1 | 56545, 89875
2 | 545665, 12524
I've tried using a Join, but I'm a bit lost. I have been attempting to do a subselect, but the only way I can think of to make it work is to reference the main query from within the subselect, but that aint gettin' it either.
This query I have been trying just hangs:
select distinct a.id,(
select b.somevalue2 + ', ' as [text()] from tableB b
where b.id = a.id and b.somevalue2 is not null for xml path(''))
as [ColumnName]
from tableA a
order by a.id asc
EDIT
I think I need to mention that TableB has over 400,000 rows.
TableA has only about 1,500 rows.
This works:
SELECT A.*,
STUFF((
SELECT ', ' + CAST([somevalue2] AS VARCHAR(20))
FROM TableB B
WHERE A.id = B.id
FOR XML PATH ('')), 1, 1, '')
FROM TableA A
Here is an sqlfiddle for you to try.
I have a table like
TABLEX -
+------+------------+
| NAME | TABLE_NAME |
+------+------------+
| X1 | X001 |
| X2 | X002 |
+------+------------+
This table contains a name column which is nothing but description and a table_name column which is actually a table already present in the database.
X001 Table has columns like X1_A, X1_B
X002 Table has columns like X2_A, X2_B
Now I want to concatenate all columns in the actual table present in the TABLE_NAME column in a comma separated string and display that as a column.
+------+------------+------------+
| NAME | TABLE_NAME | COLUMNS |
+------+------------+------------+
| X1 | X001 | X1_A, X1_B |
| X2 | X002 | X2_A, X2_B |
+------+------------+------------+
Now can this be achieved using CTE. I've already successfully created the query using STUFF with XML PATH, but I'm having performance issues because there are like 200 odd rows in the table that I've show above and each subsequent tables linked have like 100 columns each.
EDIT -
SELECT
P.NAME,
P.TABLE_NAME,
[COLUMNS]=(SELECT STUFF((SELECT ',' + NAME FROM sys.syscolumns WHERE ID = OBJECT_ID(P.TABLE_NAME) ORDER BY colorder FOR XML PATH('') ), 1, 1,''))
FROM TABLEX P
Where TABLEX is the table posted above.
Try this one -
DDL:
IF OBJECT_ID (N'dbo.TABLEX') IS NOT NULL
DROP TABLE TABLEX
IF OBJECT_ID (N'dbo.X001') IS NOT NULL
DROP TABLE X001
IF OBJECT_ID (N'dbo.X002') IS NOT NULL
DROP TABLE X002
CREATE TABLE dbo.TABLEX (NAME VARCHAR(50), TABLE_NAME VARCHAR(50))
INSERT INTO dbo.TABLEX (NAME, TABLE_NAME)
VALUES ('X1', 'X001'), ('X2', 'X002')
CREATE TABLE dbo.X001 (X1_A VARCHAR(50), X1_B VARCHAR(50))
CREATE TABLE dbo.X002 (X2_A VARCHAR(50), X2_B VARCHAR(50))
Query:
;WITH cte AS
(
SELECT
NAME
, TABLE_NAME
, [COLUMN] = CAST('' AS VARCHAR(1024))
, POS = 1
FROM TABLEX t
UNION ALL
SELECT
t.NAME
, t.TABLE_NAME
, CAST([COLUMN] + ', ' + c.name AS VARCHAR(1024))
, POS + 1
FROM cte t
JOIN sys.columns c ON
OBJECT_ID('dbo.' + t.TABLE_NAME) = c.[object_id]
AND
t.POS = c.column_id
)
SELECT
NAME
, TABLE_NAME
, [COLUMNS] = STUFF([COLUMN], 1, 2, '')
FROM (
SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY POS DESC)
FROM cte
) t
WHERE t.rn = 1
Results:
NAME TABLE_NAME COLUMNS
------ ------------- -------------
X1 X001 X1_A, X1_B
X2 X002 X2_A, X2_B
Query cost:
Statistic:
Query Presenter Scans Logical Reads
------------------- ----- -------------
XML 5 9
CTE 3 48