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;
Related
I've been on this for hours trying to figure this one out. So I need to concatenate values in a single column based on its ID's.
I have 3 tables named Type, DeliverType and Platform. I need to get all the matching ID's in the table Platform and concatenate all of its values in another column of table Platform.
Example :
Table Type Table DeliverType Table Platform
----------------------------------------------------------------------------------------
ID | Type ID | DeliverType ID | TypeID | DeliverType
----------------------------------------------------------------------------------------
1 | TestType1 1 | DeliverType1 9 | 3 | 1
2 | TestType2 2 | DeliverType2 9 | 4 | 2
3 | TestType3 3 | DeliverType3 23 | 2 | 4
4 | TestType4 4 | DeliverType4 23 | 1 | 3
These are my tables , I want to return the name of Type, the name of DeliverType and the ID of Platform
Expected Result
Table Platform
ID | NamesConcat
9 | TestType1,TestType2,TestType3,DeliverType1,DeliverType2,9
23 | TestType2,TesType3,TestType4,DeliverType3,DeliverType4,23
So it has only 1 ID which is 9 and all of the testTypes and DeliverTypes correspondant to that ID in table platform needs to be concat.
Here's my code so far:
SELECT DISTINCT p.ID,
SUBSTRING(
(
SELECT ','+ d.DeliverType,a.Type AS [text()]
FROM dbo.DeliverType d
INNER JOIN dbo.Type a ON a.ID = p.TypeID
ORDER BY d.ID
FOR XML PATH ('')
), 2, 1000) [NamesConcat]
FROM dbo.Platform p
Would appreciate some help on this one fellow programmers.
Thank you very much in advance.
From the sample data you show, I don't get the same expected result. I would expect the result:
Table Platform
ID
NamesConcat
9
TestType3,TestType4,DeliverType1,DeliverType2,9
23
TestType1,TestType2,DeliverType3,DeliverType4,23
If that is indeed the expected result and you want the strings concatenated by the Type first and then by DeliverType you can do that with this query:
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM Platform p
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
If you don't need the concatenated values in that order, you could do it with a shorter query:
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
So that you can check the work, here is a simple script that I wrote to mimic your demo data and the result:
DECLARE #Type AS TABLE (
ID INT IDENTITY(1,1) NOT NULL,
[Type] VARCHAR(20) NOT NULL
);
INSERT INTO #Type([Type]) VALUES('TestType1'), ('TestType2'), ('TestType3'), ('TestType4');
DECLARE #DeliverType AS TABLE (
ID INT IDENTITY(1, 1) NOT NULL,
[DeliverType] VARCHAR(25) NOT NULL
);
INSERT INTO #DeliverType([DeliverType]) VALUES ('DeliverType1'), ('DeliverType2'), ('DeliverType3'), ('DeliverType4');
DECLARE #Platform AS TABLE (
ID INT NOT NULL,
TypeID INT NOT NULL,
DeliverTypeID INT NOT NULL
);
INSERT INTO #Platform(ID, TypeID, DeliverTypeID) VALUES (9, 3, 1), (9, 4, 2), (23, 2, 4), (23, 1, 3);
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM #Platform p
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
I hope this is what you were looking for!
I want to join 2 tables but there is no join table between them... I usually use STRING_SPLIT but in this case, I can't figure it out. Maybe I'm just tired... Could you help me please ?
CREATE TABLE ##Provider
(
id INT,
p_name VARCHAR(50),
list_id_dep VARCHAR(250)
)
CREATE TABLE ##Department
(
id INT,
d_name VARCHAR(50)
)
INSERT INTO ##Provider (id, p_name, list_id_dep) VALUES
(1, 'toto', '/10/11/12/'),
(2, 'tata', '/09/');
INSERT INTO ##Department (id, d_name) VALUES
(9, 'dep9')
,(10, 'dep10')
,(11, 'dep11')
,(12, 'dep12');
What I want is :
id | p_name | d_name
--------------------------
1 | toto | dep10
1 | toto | dep11
1 | toto | dep12
2 | tata | dep09
I've tried :
select *
from ##Provider p
inner join ##Department d on STRING_SPLIT(p.list_id_dep, '/') = ???
select *
from ##Provider p
inner join STRING_SPLIT(p.list_id_dep, '/') dep ON dep.value = ???
select *
from ##Provider p, ##Department d
where (select value from STRING_SPLIT(p.list_id_dep, '/')) = d.id
select *
from ##Provider p, ##Department d
where d.id in (select value from STRING_SPLIT(p.list_id_dep, '/'))
Maybe STRING_SPLIT is not the right way to do it...
Thanks !
You need a lateral join to unnest the string - in SQL Server, this is implented with cross apply. Then, you can bring the department table with a regular join:
select p.id, p.p_name, d.d_name
from ##provider p
cross apply string_split(p.list_id_dep, '/') x
inner join ##department d on d.id = x.value
Demo on DB Fiddle:
id | p_name | d_name
-: | :----- | :-----
1 | toto | dep10
1 | toto | dep11
1 | toto | dep12
2 | tata | dep9
I'm working on a query to return a report that will group log records per business unit along with a comma separated list of managers assigned to each business unit, so basically there are 4 tables.
[dbo].[Log]
[dbo].[BusinessUnit]
[dbo].[Manager]
[dbo].[BusinessUnitManager]
BusinessUnit, Manager, and BusinessUnitManager for the sake of the report are only tables to gain access to basic info, the real info I need to group is contained in the Log table, my final result set should look like this:
BusinessUnit.Name,
Log.Code,
Count(*) LogRecordsCount,
Emails (Comma separated list of Managers assigned to the Business Unit)
I've been trying to get the comma separated column using CROSS APPLY but can't get it working, my current query looks like this:
SELECT
L.BusinessUnit,
L.Code,
COUNT(*) AS RecordsCount,
F.Emails
FROM [dbo].[LOG] AS L
INNER JOIN [dbo].[BusinessUnit] AS D ON L.BusinessUnit = D.ID
INNER JOIN [dbo].[BusinessUnitManager] AS DSO ON D.ID = DSO.BusinessUnit
CROSS APPLY
(
SELECT STUFF((
SELECT ',' + O.Email
FROM [dbo].[Manager] AS O
WHERE O.Id = DSO.IdManager
ORDER BY O.Email ASC
FOR XML PATH ('')), 1, 1, '')
) F(Emails)
GROUP BY L.BusinessUnit, L.Code, F.Emails
The problem with this query is returning duplicated rows, one row per each manager email assigned to the business unit and my expected result should be only one row with a column containing a comma separated list of all managers email.
Current result:
+--------------+------+--------------+----------------------------+
| BusinessUnit | Code | RecordsCount | Emails |
+--------------+------+--------------+----------------------------+
| Americas | 00 | 21 | americasmanager1#email.com |
| Americas | 00 | 21 | americasmanager2#email.com |
| Asia | 10 | 5 | asiamanager1#email.com |
| Asia | 10 | 5 | asiamanager2#email.com |
+--------------+------+--------------+----------------------------+
Expected result:
+--------------+------+--------------+-------------------------------------------------------+
| BusinessUnit | Code | RecordsCount | Emails |
+--------------+------+--------------+-------------------------------------------------------+
| Americas | 00 | 21 | americasmanager1#email.com,americasmanager2#email.com |
| Asia | 10 | 5 | asiamanager1#email.com,asiamanager2#email.com |
+--------------+------+--------------+-------------------------------------------------------+
I'll appreciate your help
You are close, just do this in two steps:
1) store your stuff into a temp table
SELECT
L.BusinessUnit,
L.Code,
O.Emails
into #tmp
FROM [dbo].[LOG] AS L
INNER JOIN [dbo].[BusinessUnit] AS D ON L.BusinessUnit = D.ID
INNER JOIN [dbo].[BusinessUnitManager] AS DSO ON D.ID = DSO.BusinessUnit
JOIN [Manager] AS O ON O.Id = DSO.IdManager
GROUP BY L.BusinessUnit, L.Code, O.Emails
2) use outer apply on yourself to stuff emails in one column
select BusinessUnit,Code, F.Emails from #tmp t
outer apply(
SELECT STUFF(( SELECT ',' + inner.Email
FROM #tmp inner
WHERE t.Id = inner.
ORDER BY O.Email ASC
FOR XML PATH ('')), 1, 1, '')
) F(Emails)
group by BusinessUnit,Code
SELECT
L.BusinessUnit,
L.Code,
COUNT(*) AS RecordsCount,
(
SELECT STUFF((
SELECT ',' + O.Email
FROM [dbo].[Manager] AS O
INNER JOIN [dbo].[BusinessUnitManager] AS DSO ON O.Id = DSO.IdManager
WHERE
DSO.BusinessUnit = D.ID
ORDER BY O.Email ASC
FOR XML PATH ('')), 1, 1, '')
) as Emails
FROM [dbo].[LOG] AS L
INNER JOIN [dbo].[BusinessUnit] AS D ON L.BusinessUnit = D.ID
GROUP BY L.BusinessUnit, L.Code, D.ID
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;
select T1.C1
,T1.C2
,T2.C2
from table1 T1
join table2 T2
on T1.C1 = T2.C1
and T1.C2 != T2.C2
and T2.C1 != ''
Output:
| T1.C1 | T1.C2 | T2.C2 |
--------------------------
| 1 | A1 | B14 |
| 1 | B14 | A1 |
| 2 | A3 | B14 |
| 2 | B14 | A3 |
Simple SQL query to return all C1 that are in two different items.
How can i remove all the duplicates from query to get this result:
| T1.C1 | T1.C2 | T2.C2 |
--------------------------
| 1 | A1 | B14 |
| 2 | A3 | B14 |
Instead of:
T1.C2 != T2.C2
use:
T1.C2 <= T2.C2
This works as long as for each (T1.C2 < T2.C2) pair an equivalent (T1.C2 > T2.C2) pair exists, like in your sample data, e.g. for (A1, B14) pair (B14, A1) also exists.
Otherwise, you can use:
SELECT T1.C1, T1.C2, T2.C2
FROM (
SELECT T1.C1, T1.C2, T2.C2,
ROW_NUMBER() OVER (PARTITION BY T1.C1,
IIF(T1.C2 <= T2.C2, T1.C2, T2.C2),
IIF(T1.C2 <= T2.C2, T2.C2, T1.C2)
ORDER BY T1.C2, T2.C2) AS rn
FROM table1 T1
JOIN table2 T2
ON T1.C1 = T2.C1
AND T1.C2 != T2.C2
AND T2.C1 != '') AS t
WHERE t.rn = 1
If I understand what you're after, here is the code to generate this output:
T1xC2 hits
-------------------- -------------------
A1 A3,B14
A3 A1,B14
B14 A1,A3
Is Produced by:
Declare #T1 Table (C1 int, C2 varchar(20))
Declare #T2 Table (C1 int, C2 varchar(20))
insert into #T1
Select 1, 'A1'
union select 1, 'B14'
union select 2, 'A3'
union select 2, 'B14'
Insert into #T2
Select 1, 'B14'
Union Select 1, 'A1'
union select 2, 'B14'
union select 2, 'A3'
;WITH mix
AS (
SELECT T1.C1 AS [T1xC1]
,T2.C1 AS [T2xC1]
,T1.C2 AS [T1xC2]
,T2.C2 AS [T2xC2]
,ROW_NUMBER() OVER (
ORDER BY (
SELECT NULL
)
) AS rnk
FROM #T1 T1
CROSS JOIN #t2 T2
)
,Groupwork
AS (
SELECT *
FROM mix m
WHERE EXISTS (
SELECT 1
FROM Mix m2
WHERE m.T1xC2 = m2.T1xC2
AND m.T2xC2 <> m2.T2xC2
AND m.T1xC2 <> m.t2xc2
)
)
,GroupRows
AS (
SELECT DISTINCT T1xc1, T1xC2
FROM Groupwork
)
SELECT distinct T1xC2, x.hits
FROM GroupRows g
CROSS APPLY (
SELECT STUFF((
SELECT distinct ',' + g2.T2xC2
FROM Groupwork g2
WHERE g2.T1xC2 = g.T1xC2
FOR XML PATH('')
), 1, 1, '') hits
) x