join by column type - sql-server

I have 2 tables like the following.
id1 | val | type
1 2 type1
1 4 type2
2 9 type2
2 7 type1
id2|type1|type2
11 2 4
33 7 9
I need result like this
id1|id2
1 11
2 33
I need to check both type1 and type2 to relate id1 and id2. I tried the following query but it does not work.
select id1,id2 from t1 inner join t2 on (type='type1' and
t1.val=t2.type1)and (type='type2' and t1.val=t2.type2)

I believe this should give you what you want:
SELECT
a.id1,
t2.id2
FROM
t2
INNER JOIN
t1 a ON a.val = t2.type1 AND a.type = 'type1'
INNER JOIN
t1 b ON b.val = t2.type2 AND b.type = 'type2'
WHERE
a.id1 = b.id1

SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE t1(id1 INT, val INT, [type] VARCHAR(10))
INSERT INTO t1 VALUES
(1 , 2 ,'type1'),
(1 , 4 ,'type2'),
(2 , 9 ,'type2'),
(2 , 7 ,'type1')
CREATE TABLE t2(id2 INT, [type1] VARCHAR(10), [type2] VARCHAR(10))
INSERT INTO t2 VALUES
(11 , 2 , 4),
(33 , 7 , 9)
Query 1:
Select t1.id1 , t2.id2
from t1
inner join
(Select * from t2 t
UNPIVOT (Val for [type] IN ([type1],[type2]))up) t2
ON t1.val = t2.Val and t1.type = t2.type
GROUP BY t1.id1 , t2.id2
Results:
| id1 | id2 |
|-----|-----|
| 1 | 11 |
| 2 | 33 |

Related

TSQL How to concatenate column from joined table with group by

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;

Avoid Cross Joins in SQL Server

I have 2 tables T1 and T2.
T1:
ID | Name
----+-------
A | A1
A | C1
T2:
ID | Name
-----+------
A | A1
A | B1
I want to retrieve records that have same ID and Name with flag 1 and Same ID and Different Name with flag 0. However, while joining the table in SQL Server, I am getting the a cross join which is:
A | A1 | A1 | 1
A | A1 | B1 | 0
A | C1 | A1 | 0
A | C1 | B1 | 0
But I need the answer as:
A | A1 | A1 | 1
A | C1 | B1 | 0
The above result is giving me the same information about name mismatch but in limited no. of rows and no repetition.
Could somebody let me know how can do this in SQL Server?
Is this what you're after:
SELECT T1.ID, T1.Name Name1, T2.Name Name2, case T1.Name when T2.Name then 1 else 0 end Result
from T1
inner join T2 on T1.ID = T2.ID
where T1.Name = T2.Name
or (not exists (select 1 from T2 where T1.Name = Name and T1.ID = ID)
and not exists (select 1 from T1 where T2.Name = Name and T2.ID = ID))
Use a union to keep things simple:
select T1.ID, T1.Name Name1, T2.Name Name2, 1 flag
from T1
join T2 on T1.ID = T2.ID
and T1.Name = T2.Name
union all
select T1.ID, T1.Name, T2.Name, 0
from T1
join T2 on T1.ID = T2.ID
and T1.Name != T2.Name
Using a union is not the most efficient way, but it’s much easier to understand and unless you have millions of rows, it will still run very fast (and union all is quite a bit faster than union)
you can use ROW_NUMBER and FULL JOIN
DECLARE #T1 TABLE (ID VARCHAR(5), Name VARCHAR(5))
INSERT INTO #T1 VALUES ('A', 'A1')
INSERT INTO #T1 VALUES('A', 'C1')
DECLARE #T2 TABLE (ID VARCHAR(5), Name VARCHAR(5))
INSERT INTO #T2 VALUES ('A', 'A1')
INSERT INTO #T2 VALUES ('A', 'B1')
SELECT T1.ID, T1.Name, T2.Name, CASE WHEN T1.Name = T2.Name THEN 1 ELSE 0 END
FROM
( SELECT ROW_NUMBER()OVER(PARTITION BY ID ORDER BY Name) AS RN, * FROM #T1 ) T1
FULL JOIN
( SELECT ROW_NUMBER()OVER(PARTITION BY ID ORDER BY Name) AS RN, * FROM #T2 ) T2
ON T1.ID = T2.ID AND T1.RN = T2.RN
Result:
ID Name Name
----- ----- ----- -----------
A A1 A1 1
A C1 B1 0

Full Outer Join self with null values

I want to do a full outer self-join that includes nulls. For example, if the table Data looks like:
N Name Val
--------------
1 ABC 8
1 DEF 7
2 ABC 9
2 XYZ 6
(where N is a general index column to enable a self-join on sequential groups) and I do:
SELECT COALESCE(a.n, b.n) as n, COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM Data a
FULL OUTER JOIN Data b on a.N = b.N - 1 and a.Name = b.Name
I want:
N Name A B
---------------
1 ABC 8 9
1 DEF 7 NULL
1 XYZ NULL 6
but what I get is more like a cross-join:
n Name A B
--------------
1 ABC 8 9
1 DEF 7 NULL
2 ABC 9 NULL
2 XYZ 6 NULL
1 ABC NULL 8
1 DEF NULL 7
2 XYZ NULL 6
How do I perform this full outer join in order to get the condensed self-join results?
(Note: In practice column N is a generalized index, so solutions that require naming the values of N aren't practical.)
So far I've been only able to see doing this as a union. and a left and right join since the criteria of what you're after changes.
SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM Data a
LEFT JOIN Data b on a.Name = b.Name
and B.N = 2
WHERE A.N = 1
UNION
SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM Data a
RIGHT JOIN Data b on a.Name = b.Name
and A.N = 1
WHERE B.N = 2
Giving us:
+------+---+----+
| NAME | A | B |
+------+---+----+
| ABC | 8 | 9 |
| DEF | 7 | |
| XYZ | | 6 |
+------+---+----+
However this relies on a hardcoded N value which I don't think is that useful... working on better.
Since we want to handle a generalized self-join index column N let's extend the sample set a little further:
create table #Data (n int, name char(3), val int)
insert into #Data values (1, 'ABC',8)
insert into #Data values (1, 'DEF',7)
insert into #Data values (2, 'ABC',9)
insert into #Data values (2, 'XYZ',6)
insert into #Data values (3, 'ABC',9)
insert into #Data values (3, 'DEF',5)
insert into #Data values (3, 'XYZ',4)
For this sample we want the SQL to produce this output:
N Name A B
---------------
1 ABC 8 9
1 DEF 7 NULL
1 XYZ NULL 6
2 ABC 9 9
2 DEF NULL 5
2 XYZ 6 4
The following code works on the general case:
SELECT COALESCE(a.n, b.n-1) as i, COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM #Data a
FULL OUTER JOIN #Data b ON a.N = b.N - 1 AND a.Name = b.Name
WHERE a.n < (SELECT MAX(n) FROM #Data) -- Deals with end index case
OR (a.n is null AND b.n-1 IN (SELECT DISTINCT n FROM #Data))
ORDER BY COALESCE(a.n, b.n-1), Name
To see why this works, a good intermediate step is to note that when a.N = 1 we want the rows where n = 1 from:
SELECT COALESCE(a.n, b.n - 1) as n, COALESCE(a.Name, b.Name) as Name,
a.Val as A, b.Val as B
FROM #Data a
FULL OUTER JOIN #Data b ON a.N = b.N - 1 AND a.Name = b.Name
Please see the code below:
create table Data (n int, name char(3), val int)
insert into data values (1, 'ABC',8)
insert into data values (1, 'DEF', 7)
insert into data values (2 , 'ABC' , 9)
insert into data values (2 , 'XYZ', 6)
SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM Data a
FULL OUTER JOIN Data b on a.N = b.N - 1 and a.Name = b.Name
The output is this:
There are nulls on both sides.
Maybe this:
SELECT [Name]
,[1]
,[2]
FROM [table]
PIVOT
(
MAX([val]) FOR [N] IN ([1], [2])
) PVT;

How can I combine two result sets by a common column?

I feel like there should be an easy way to do this.
Given two tables (ID is primary key, no duplicates):
TblQtyNew TblQtyUsed
ID | QtyNew ID | QtyUsed
1 15 1 7
2 18 3 21
How can I obtain the following result?
ID | QtyNew | QtyUsed
1 15 7
2 18 NULL
3 NULL 21
The only solution I have come up with involves a UNION on the ID column then two left joins:
(SELECT ID FROM TblQtyNew) UNION (SELECT ID FROM TblQtyUsed) as IDs
LEFT JOIN
(SELECT QtyNew FROM TblQtyNew) ON TblQtyNew.ID = IDs.ID
LEFT JOIN
(SELECT QtyUsed FROM TblQtyUsed) ON TblQtyUsed.ID = IDs.ID
Is there a more simple way to do this?
You can use full join and coalesce in id as below:
select coalesce(t1.id,t2.id) as Id, Qtynew, QtYused
from #table1 t1 full join #table2 t2
on t1.id = t2.id
Output:
+----+--------+---------+
| Id | Qtynew | QtYused |
+----+--------+---------+
| 1 | 15 | 7 |
| 2 | 18 | NULL |
| 3 | NULL | 21 |
+----+--------+---------+
You want a FULL OUTER JOIN:
SELECT COALESCE(T1.ID,T2.ID) as ID, T1.QtyNew, T2.QtyUsed
FROM TblQtyNew T1
FULL OUTER JOIN TblQtyUsed T2 on T1.ID = T2.ID
1.
SELECT ID,SUM(ISNULL(QtyNew)) AS QtyNew,SUM(ISNULL(QtyUsed)) AS QtyUsed
FROM (
SELECT ID,QtyNew,NULL AS QtyUsed FROM TblQtyNew
UNION ALL
SELECT ID,NULL QtyNew, QtyUsed FROM TblQtyUsed
) AS t
GROUP BY ID
2.
SELECT COALESCE(n.ID,U.ID) AS ID,n.QtyNew,u.QtyUsed
FROM TblQtyNew AS n FULL OUTER JOIN TblQtyUsed AS u ON n.ID=u.ID
Use FULL JOIN and Case:
DECLARE #T1 TABLE (id int , QtyNew int);
DECLARE #T2 TABLE( id int , QtyUsed int);
insert into #T1 values (1,15),(2,18);
insert into #T2 values (1,7),(3,21);
SELECT ID = case when T1.ID IS not Null then T1.ID else T2.ID end, T1.QtyNew, T2.QtyUsed
FROM #T1 T1
full JOIN #T2 T2 on T1.ID = T2.ID
Order by ID;
Demo
I intentionally trying alternate method.
declare #TblQtyNew table (ID int,QtyNew int)
insert into #TblQtyNew VALUES
(1,15)
,(2,18)
declare #TblQtyUsed table ( ID int, QtyUsed int)
insert into #TblQtyUsed VALUES
(1 ,7 )
,(3 ,21)
;with CTE as
(
select a.id,QtyNew,b.QtyUsed from #TblQtyNew a
inner join #TblQtyUsed b on a.id=b.ID
)
select * from CTE
union all
select a.id,QtyNew,null
from #TblQtyNew a
where not exists(select id from cte c where c.id=a.id)
union all
select a.id,null,QtyUsed
from #TblQtyUsed a
where not exists(select id from cte c where c.id=a.id)
select Id, QtyNew, QtyUsed
from TblQtyNew Full outer join TblQtyUsed
on TblQtyNew.ID=TBLQtyUsed.ID

Join two different columns from two different tables and merge duplicates

i have two temporary table
Table 1
ID1 Name ID2 Single
----------------------------------------------------
1 ABC 1 100
2 DEF 1 200
Table 2
ID1 Name ID2 Monthly
----------------------------------------------------
3 PQR 2 500
4 LMN 2 600
1 ABC 2 700
2 DEF 2 800
I want Output
ID1 Name ID2 Single Monthly
--------------------------------------------------------
1 ABC 1 100 700
2 DEF 1 200 800
3 PQR 2 NULL 500
4 LMN 2 NULL 600
I used all Joins , Union ALL , Union nothing working
thanks in advance
Try this:
select coalesce(T1.ID1, T2.ID1) as ID1,
coalesce(T1.Name, T2.Name) as ID1,
coalesce(T1.ID2, T2.ID2) as ID2,
T1.Single,
T2.Monthly
from Table1 as T1
full outer join Table2 as T2
on T1.ID1 = T2.ID1
https://data.stackexchange.com/stackoverflow/q/121659/
If you know that all rows always will be present in Table2 you can use a right outer join instead of full join.
Hope you are using Sql Server 2008(other wise the insert statement in my query won't work). Try this one.
From the required out put, i guess you need all the values from table2 and there corresponding Single(Column name in table 1) value.
DECLARE #tempTable1 TABLE (ID1 INT,Name VARCHAR(10),ID2 INT,Single INT)
DECLARE #tempTable2 TABLE (ID1 INT,Name VARCHAR(10),ID2 INT,Monthly INT)
INSERT INTO #tempTable1 VALUES
(1 ,'ABC' ,1 ,100),
(2 ,'DEF' ,1 ,200)
INSERT INTO #tempTable2 VALUES
(3 ,'PQR' ,2 ,500 ),
(4 ,'LMN' ,2 ,600 ),
(1 ,'ABC' ,2 ,700 ),
(2 ,'DEF' ,2 ,800 );
SELECT
T2.ID1
,T2.Name
,T2.ID2
,T1.Single
,T2.Monthly
FROM #tempTable2 T2
LEFT OUTER JOIN #tempTable1 T1
ON T2.ID1 = T1.ID1
ORDER BY T2.ID1

Resources