Left join from 2 tables with same ID - sql

I have tabl1e1 and table2 with data
table1
location costA
a 5
a 10
a 15
b 11
b 12
table2
Location CostB
a 100
b 100
My goal to get the result
location costA costB
a 5 100
a 10
a 15
b 11 50
b 12
My query
select T1.location, T1.cost
from (
select location, cost
, row_number() over ( partition by location order by cost) rownumber
from table1
) T1 left join (
select location, cost
, row_number() over ( partition by location order by cost ) rownumber
from table2
) T2 on T2.location = T2.cost and T1.rownumber = T2.rownumber
I got
location costA missing costB column
a 5
a 10
a 15
b 11
b 12
Not sure why but can you point out the missing one. Thank you.

First of all you are expecting three columuns in result and your select statement contains only 2.
select T1.Location, T1.Cost
2nd the join should be
T2 on T1.[location] = T2.[location] and T1.rownumber = T2.rownumber
Below is the complete working example
DECLARE #table1 as table
(
[location] char,
costA int
)
DECLARE #table2 as table
(
[location] char,
costB int
)
INSERT INTO #table1
VALUES
('a', 5)
,('a', 10)
,('a', 15)
,('b', 11)
,('b', 12)
INSERT INTO #table2
VALUES
('a', 100)
,('b', 100)
select T1.[location], T1.costA, T2.costB
from (
select [location], costA
, row_number() over ( partition by location order by costA) rownumber
from #table1
) T1 left join (
select [location], costB
, row_number() over ( partition by location order by costB ) rownumber
from #table2
) T2 on T1.[location] = T2.[location] and T1.rownumber = T2.rownumber

The join
T2 on T2.location = T2.cost and T1.rownumber = T2.rownumber
should be on
T2 on T1.location = T2.location and T1.rownumber = T2.rownumber
select
T1.location,
T1.costA,
T2.costB
from (
select
location,
costA,
row_number() over ( partition by location order by costA) rownumber
from table1
) T1
left join (
select
location,
costB,
row_number() over ( partition by location order by costB ) rownumber
from table2
) T2
on T1.location = T2.location and T1.rownumber = T2.rownumber
GO
location | costA | costB
:------- | ----: | ----:
a | 5 | 100
a | 10 | null
a | 15 | null
b | 11 | 100
b | 12 | null
db<>fiddle here

Related

TSQL - Fill missing records

Code:
CREATE TABLE #Temp
(
[ID1] INT
, [ID2] INT
, [ID3] INT
, [Val] BIT
, PRIMARY KEY CLUSTERED ( [ID1], [ID2], [ID3] )
) ;
INSERT INTO #Temp
SELECT 1
, 10
, 100
, 1
UNION ALL
SELECT 1
, 10
, 101
, 1
UNION ALL
SELECT 1
, 11
, 100
, 1
UNION ALL
SELECT 1
, 11
, 101
, 1
UNION ALL
SELECT 2
, 10
, 100
, 1 ;
CREATE TABLE #Temp_ID3
(
[ID3] INT
) ;
INSERT INTO #Temp_ID3
SELECT 100
UNION ALL
SELECT 101
UNION ALL
SELECT 102 ;
SELECT [ID1]
, [ID2]
, [ID3]
, [Val]
FROM #Temp ;
SELECT [ID3]
FROM #Temp_ID3 ;
DROP TABLE #Temp_ID3 ;
DROP TABLE #Temp ;
Output:
ID1 ID2 ID3 Val
1 10 100 1
1 10 101 1
1 11 100 1
1 11 101 1
2 10 100 1
Goal:
To find missing ID3 records - from #Temp_ID3 table - in #Temp table (ID1/ID2/ID3 combos for existing ID1/ID2 combos). For those missing records, Val should be False. Desired Output will make sense.
I can get distinct ID1/ID2 from #Temp, cross join with ID3 and create a dataset. Left join #Temp to this new dataset, and insert records that way, but looking for a more "simpler" way.
Here's the "complicated" way.
;WITH CTE AS
(
SELECT DISTINCT
[TT].[ID1]
, [TT].[ID2]
, [T3].[ID3]
FROM #Temp_ID3 AS [T3]
CROSS JOIN (
SELECT DISTINCT
[ID1]
, [ID2]
FROM #Temp
) AS [TT]
)
SELECT [C].[ID1]
, [C].[ID2]
, [C].[ID3]
, COALESCE ( [TT].[Val], 0 ) AS [Val]
FROM CTE AS [C]
LEFT JOIN #Temp AS [TT]
ON [C].[ID1] = [TT].[ID1]
AND [C].[ID2] = [TT].[ID2]
AND [C].[ID3] = [TT].[ID3]
ORDER BY [C].[ID1]
, [C].[ID2]
, [C].[ID3] ;
Desired Output:
ID1 ID2 ID3 Val
1 10 100 1
1 10 101 1
1 10 102 0 -- Filling Missing ID3 Record w/ default Val = 0
1 11 100 1
1 11 101 1
1 11 102 0 -- Filling Missing ID3 Record w/ default Val = 0
2 10 100 1
2 10 101 0 -- Filling Missing ID3 Record w/ default Val = 0
2 10 102 0 -- Filling Missing ID3 Record w/ default Val = 0
Use a cross join to generate the rows and then left join to fill in the values:
select t1.id1, t2.id2, t3.id3, coalesce(t.val, 0) as val
from (select distinct id1 from #temp) t1 cross join
(select distinct id2 from #temp) t2 cross join
(select distinct id3 from #temp) t3 left join
#temp t
on t.id1 = t1.id1 and t.id2 = t2.id2 and t.id3 = t3.id3;
You don't have to use subqueries to generate the ids, if one of the tables actually has them.
EDIT:
For the revised version in the comment, you can do something very similar:
select t12.id1, t12.id2, t3.id3, coalesce(t.val, 0) as val
from (select distinct id1, id2 from #temp) t12 cross join
#temp_id3 t3 left join
#temp t
on t.id1 = t12.id1 and t.id2 = t12.id2 and t.id3 = t3.id3;
As per example you want only cross join with self join
select distinct t.id1, t.id2, t3.id3, coalesce(t1.val, 0) as Val
from #Temp t cross join #Temp_ID3 t3
left join #temp t1
on t1.id1 = t.id1 and t1.id2 = t.id2 and t1.id3 = t3.id3;

SQL Server Join two tables without duplicates in primary table

I have two tables that I want to join together such that all foreign rows are returned and the primary table's rows are not duplicated. For example:
T1
pk code value
1 One 100
2 Two 200
T2
fk value
1 10
1 15
1 30
2 25
I want all records of T2 without the T1 records duplicating, so the result set I want to look like this:
T2.fk T1.code T1.value T2.value
1 One 100 10
1 NULL NULL 15
1 NULL NULL 30
2 Two 200 25
Is there a SQL Server join method for achieving that?
You need to rank your rows in T2 and do a left join including rank as a join condition:
with cte as(select *, row_number() over(partition by fk order by value) as rn from T2)
select c.fk, t.code, t.value, c.value
from cte c
left join T1 t on c.fk = t.pk and c.rn = 1
Here is the full example:
DECLARE #t1 TABLE
(
pk INT ,
code VARCHAR(MAX) ,
value INT
)
INSERT INTO #t1
VALUES ( 1, 'One', 100 ),
( 2, 'Two', 200 )
DECLARE #t2 TABLE ( fk INT, value INT )
INSERT INTO #t2
VALUES ( 1, 10 ),
( 1, 15 ),
( 1, 30 ),
( 2, 25 );
WITH cte
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY fk ORDER BY value ) AS rn
FROM #t2
)
SELECT c.fk ,
t.code ,
t.value ,
c.value
FROM cte c
LEFT JOIN #t1 t ON c.fk = t.pk
AND c.rn = 1
Try this:
select T2.fk,
CASE
WHEN (SELECT COUNT(*) FROM t2 tother WHERE tother.fk = t2.fk
AND tother.value > t2.value) > 0 THEN NULL ELSE t1.code
END,
CASE
WHEN (SELECT COUNT(*) FROM t2 tother WHERE tother.fk = t2.fk
AND tother.value > t2.value) > 0 THEN NULL ELSE t1.value
END,T2.value
from t2
join t1
on t2.fk = t1.pk
DECLARE #t1 TABLE (pk int,code varchar(10),value int)
DECLARE #t2 TABLE (fk int,value int)
INSERT INTO #t1
SELECT 1,'one',100
UNION
SELECT 2,'two',200
INSERT INTO #t2
SELECT 1,10
UNION SELECT 1,15 UNION SELECT 1,30 UNION SELECT 2,25
;WITH cte AS(
SELECT t2.fk,t2.value t2val,t1.pk,t1.code,t1.value t1val,ROW_NUMBER() OVER(PARTITION BY fk ORDER BY fk) rno FROM #t2 t2 LEFT JOIN #t1 t1 on t2.fk=t1.pk)
SELECT fk,code=(CASE WHEN rno=1 THEN code ELSE null END),t1val=(CASE WHEN rno=1 THEN t1val ELSE NULL END),t2val FROM cte
output
fk code t1val t2val
1 one 100 10
1 NULL NULL 15
1 NULL NULL 30
2 two 200 25

TSQL Distinct Counts

I have a table that looks like this:
ID SuppressionTypeID PersonID
------------------------------
1 1 123
2 1 456
3 2 456
I want to get a rolling count (distinct people) rather than a normal group by count.
e.g. not this:
SuppressionTypeID Count
---------------------------
1 2
2 1
This:
SuppressionTypeID RecordsLost
----------------------------------
1 2
2 0
The latter being zero as we lost person 456 on suppresiontypeid 1.
Thanks in advance.
You may need to use a temporary table or a table variable as shown below
DECLARE #t TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
)
INSERT INTO #t
SELECT 1
,1
,123
UNION ALL
SELECT 2
,1
,456
UNION ALL
SELECT 3
,2
,456
DECLARE #t1 TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
,firstid INT
)
INSERT INTO #t1
SELECT *
,NULL
FROM #t
UPDATE t1
SET t1.firstid = t2.firstid
FROM #t1 AS t1
INNER JOIN (
SELECT personid
,min(SuppressionTypeID) AS firstid
FROM #t1
GROUP BY personid
) AS t2 ON t1.PersonID = t2.PersonID
SELECT coalesce(t2.firstid, t1.SuppressionTypeID) AS SuppressionTypeID
,count(DISTINCT t2.personid) AS count
FROM #t1 AS t1
LEFT JOIN #t1 AS t2 ON t1.personid = t2.personid
AND t1.SuppressionTypeID = t2.firstid
GROUP BY coalesce(t2.firstid, t1.SuppressionTypeID)
The result is
SuppressionTypeID count
----------------- -----------
1 2
2 0
You can try;
with tmp_tbl as (
select
x.SuppressionTypeID, count(x.PersonID) as RecordsLost
from (
select
min(SuppressionTypeID) as SuppressionTypeID,
PersonID
from tbl
group by PersonID
) as x
group by x.PersonID
order by x.SuppressionTypeID
)
select
distict t.SuppressionTypeID, coalesce(tmp.RecordsLost, 0) as RecordsLost
from tbl t
left join tmp_tbl tmp on tmp.SuppressionTypeID = t.SuppressionTypeID

Get groups that are exactly equal to a table

I have a query that groups easily. I need to get the groups that have exactly the same records to another table (relationship).
I'm using ANSI-SQL under SQL Server, but I accept an answer of any implementation.
For example:
Table1:
Id | Value
---+------
1 | 1
1 | 2
1 | 3
2 | 4
3 | 2
4 | 3
Table2:
Value | ...
------+------
1 | ...
2 | ...
3 | ...
In my example, the result is:
Id |
---+
1 |
How imagined that it could be the code:
SELECT Table1.Id
FROM Table1
GROUP BY Table1.Id
HAVING ...? -- The group that has exactly the same elements of Table2
Thanks in advance!
You can try the following:
select t1.Id
from Table2 t2
join Table1 t1 on t1.value = t2.value
group by t1.Id
having count(distinct t1.value) = (select count(*) from Table2)
SQLFiddle
To get the same sets use an inner join:
SELECT Table1.Id
FROM Table1
INNER JOIN table2 ON table1.id=table2.id
GROUP BY Table1.Id
HAVING ...? --
CREATE TABLE #T1 (ID INT , [Values] INT) INSERT INTO #T1 VALUES (1,1),(1,2),(1,3),(2,4),(2,5),(3,6)
CREATE TABLE #T2 ([Values] INT) INSERT INTO #T2 VALUES (1),(2),(3),(4)
SELECT * FROM #T1
SELECT * FROM #T2
SELECT A.ID
FROM
( SELECT ID , COUNT(DISTINCT [Values]) AS Count FROM #T1
GROUP BY ID
) A
JOIN
(
SELECT T1.ID, COUNT(DISTINCT T2.[Values]) Count
FROM #T1 T1
JOIN #t2 T2
ON T1.[Values] = T2.[Values]
GROUP BY T1.ID
) B
ON A.ID = B.ID AND A.Count = B.Count

Detect range and count from a table

A table with 2 columns ordered by group, number:
group_id | number
---------+--------
1 | 101
1 | 102
1 | 103
1 | 106
2 | 104
2 | 105
2 | 107
What SQL query should I write to get the following output:
group_id | number_from | number_to | total
---------+-------------+------------+-------
1 | 101 | 103 | 3
1 | 106 | 106 | 1
2 | 104 | 105 | 2
2 | 107 | 107 | 1
Here is SQL Fiddel Demo
Below is the script
create table Temp(A int,B int);
insert into temp values (1,101);
insert into temp values (1,102);
insert into temp values (1,103);
insert into temp values (1,106);
insert into temp values (2,104);
insert into temp values (2,105);
insert into temp values (2,107);
Select T2.A "group_id",
Min(T2.B) "number_from",
Max(T2.B) "number_to",
Max(T2.E) "total"
from
(
select *,(B-C) D,
rank() over
(PARTITION by T.A,(B-C) order by T.A,T.B) E,
rank() over
(order by T.A,(B-C)) F
from
(select A,B,row_number()
over (order by (select 0)) C
from temp) T
) T2
group by T2.A,T2.D,T2.F
order by 1,2
i used this as example table:
create table temp (id int, val int)
insert into temp values (1,101),(1,102),(2,102),(2,104),(2,107)
insert into temp values (2,103)
insert into temp values (2,105)
insert into temp values (2,108)
insert into temp values (2,110)
this is what you want:
select t1id,cnt, min(t1val) as min, max(t1val), count(t1val)
from (
select tt1.*,
(select count (*) from
(
select t1.id as t1id,
t1.val as t1val,
(select val from temp t2 where t1.id = t2.id and t2.val = t1.val+1 ) as t2val,
row_number() over (order by t1.id, t1.val ) as rn
from temp t1
) tt2
where tt2.t2val is null and tt2.rn < tt1.rn
) cnt
from (
select t1.id as t1id,
t1.val as t1val,
(select val from temp t2 where t1.id = t2.id and t2.val = t1.val+1 ) as t2val,
row_number() over (order by t1.id, t1.val ) as rn
from temp t1
) tt1
)ttt1
group by t1id, cnt
order by t1id, min
update: fixed bug if table is unsorted)
WITH RECURSIVE rope AS (
SELECT i1.id AS low
, i1.id AS high
, i1.grp AS grp
, 1::integer AS cnt
FROM islands i1
-- no left neighbor
WHERE NOT EXISTS ( SELECT * FROM islands x WHERE x.grp = i1.grp AND x.id = i1.id-1)
UNION ALL
SELECT ch.low AS low
, i2.id AS high
, i2.grp AS grp
, 1+ch.cnt AS cnt
FROM islands i2
-- connect to left neighbor
JOIN rope ch ON i2.grp = ch.grp AND i2.id = ch.high+1
)
SELECT * FROM rope r
-- suppress subchains
WHERE NOT EXISTS (
SELECT * FROM rope nx
WHERE nx.low = r.low AND nx.cnt > r.cnt
)
;