join 2 tables using oracle - sql

There are 2 tables with data as below. The column 'ID' is the available in both tables. can someone please help me how do i get the desired output.
Table 1:
ID IND
101 Y
102 N
Table 2:
ID CD
101 A
101 B
101 C
101 D
102 A
Desired output:
ID CD IND
101 A Y
101 B
101 C
101 D
102 A N

What you have here is a join where you want to display the value of ind only on the first cd of each id. You can do this with the row_number() window function in a case expression:
SELECT t1.id, t2.cd, CASE rn WHEN 1 THEN t1.ind ELSE NULL END AS ind
FROM table1 t1
JOIN (SELECT id, cd, ROW_NUMBER() OVER (PARTITION BY ind ORDER BY cd ASC) AS rn
FROM table2) t2 ON t1.id = t2.id

This should do the trick.
SELECT table1.ID, table2.CD, table1.IND
FROM table1
INNER JOIN table2
ON table1.ID = table2.ID;

I would normally expect that if you had a table with a relationship in the ID column that you would want to do a simple inner join: http://sqlfiddle.com/#!4/e8e41/5
select t1.id, t2.cd, t1.ind
from t1
inner join t2
on t2.id = t1.id
However, that wouldn't give you the result you specified. Your IND field would be populated for each row.
Perhaps you're looking to join only where cd = 'A'. I don't know the point of that, but you could do something like this: http://sqlfiddle.com/#!4/e8e41/12
select t2.id, t2.cd, coalesce(t1.ind,' ') as ind
from t2
left join t1
on t2.id = t1.id
and t2.cd = 'A'
order by id, cd
Or perhaps you're looking to associate the ind with the first cd of each id. In that case, something like this (using a common table expression) might work for you: http://sqlfiddle.com/#!4/e8e41/20
with cte as (
select id, cd, row_number() over (partition by id order by cd) as rn
from t2
)
select t1.id, cte.cd, case when rn = 1 then t1.ind else ' ' end as ind
from t1
inner join cte
on t1.id = cte.id

Related

Multiple search between two tables

I have two tables - the first (this is just an example but the original query is a pretty big)
item_id_t1
serial_num_t1
country_t1
customer
snapshot_date_t1
serial_num_trunc_t1
156648107
222-99950578
AAA
BBSS
12/1/2022
99950578
156648107
222-99950578
AAA
BBSS
11/1/2022
99950578
156648107
222-99950578
AAA
BBSS
1/1/2023
99950578
108279887
888-515179765
AAA
BBSS
12/1/2022
515179765
108279887
888-515179765
AAA
BBSS
11/1/2022
515179765
108279887
888-515179765
AAA
BBSS
11/1/2023
515179765
and the second one
serial_num_trunc_t2
serial_num_t2
up_ind_t2
99950578
333-99950578
1
515179765
888-515179765
1
Now, my first step would be to find a match based on serial_num_t1 = serial_num_t2 and the second part of the code should be to find all unmatched records but this time based on serial_num_trunc_t1 = serial_num_trunc_t2.
I started with CTE tables first
WITH t1 AS (SELECT * FROM t1),
t2 AS (SELECT * FROM t2)
SELECT a.*
(SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON serial_num_t1 = serial_num_t2) a
INNER JOIN
(SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t1
WHERE q.up_ind_t2 <> 1) b
ON a.serial_num_trunc_t1 = b.serial_num_trunc_t2
and here I am stuck. How to compare unmatched values from table "a" again with table "b" based ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t2 WHERE t2.ip_ind_t2 <> 1
My final table should have six records (like t1) and "up_ind_t2" should be filled with 1 for all six records in the final table.
I would appreciate your help.
I am guessing, you want
WITH t1 AS (SELECT * FROM t1),
t2 AS (SELECT * FROM t2)
SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON serial_num_t1 = serial_num_t2
UNION ALL
SELECT t1.*, t2.*
FROM t1
INNER JOIN t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t1
WHERE q.up_ind_t2 <> 1
as Query,the LEft JOIN doesn't make that much sense in the firstplace, so i removed ot from the second.
the second query implies that you have only 1 record for every serial_num_trunc_t2 and that is not equal to 1, if not you need to add limit
#nbk
It doesn't look like my solution. I added match_level in order to see from where a match comes: from LEFT JOIN or from INNER JOIN (UNION part of the code).
So, when I run your code I get the same results as before UNION ALL part
WITH t1 AS (SELECT * FROM ib),
t2 AS (SELECT * FROM qu)
SELECT t1.*, t2.*, 1 as match_level
FROM ib t1
LEFT JOIN qu t2
ON serial_num_t1 = serial_num_t2
UNION ALL
SELECT t1.*, t2.*, 2 as match_level
FROM ib t1
INNER JOIN qu t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t2
WHERE t2.up_ind_t2 <> 1
Finally, this is what I needed.
with
ib as (
select *
from [dbo].[ib] i
left join [dbo].[qu] q
on i.serial_num_t1=q.serial_num_t2),
qu as (
select *
from [dbo].[ib] i
right join [dbo].[qu] q
on i.serial_num_trunc_t1=q.serial_num_trunc_t2)
select ff.*
from
(select *
from
(select ROW_NUMBER() OVER(PARTITION BY q.item_id_t1,
q.serial_num_t1,q.country_t1, q.customer, q.snapshot_date_t1,
q.up_ind_t2 ORDER BY q.snapshot_date_t1, q.up_ind_t2 DESC) AS it_row, q.*
from qu q
left join ib b
on q.serial_num_trunc_t2 = b.serial_num_trunc_t1) a
union
select 99 as it_row, zz.*
from qu as zz ) ff
where it_row = 1

SQL Select all first items in a list of rows identified by Ids and filtered by a specific Type in another table

I need to create a table keyed by an ID where the values of one of the columns in the new table are the earliest values entered into the column of another table where the rows share the same ID and have a specific type label.
For example, say I want the Name and first Value entered for each fruit with an entry type A:
These are the tables I have:
TABLE1
Key
ID
Name
1
1
Cherry
2
2
Grape
TABLE2
Key
ID
Value
EntryNum
EntryType
1
1
21
1
A
2
1
32
2
B
3
1
4
3
B
4
1
15
4
A
5
2
3
1
B
6
2
8
2
A
7
2
16
3
B
And this is the result that I want:
TABLE3
ID
Name
EarliestEntry
1
Cherry
21
2
Grape
8
I've attempted the following query but it just returns the same value for all EarliestEntry:
SELECT TABLE1.ID, TABLE2.Name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY TABLE2.EntryNum)
as row_num, Value FROM TABLE2
WHERE TABLE2.ID = TABLE1.ID AND TABLE2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO TABLE3
FROM TABLE2
INNER JOIN TABLE1 ON TABLE1.ID = TABLE2.ID
GROUP BY TABLE1.ID, TABLE2.Type, TABLE2.EntryNum
I would greatly appreciate help on this. Thank you
If you wanted to use the ROW_NUMBER function then you would need to put that on TABLE1 and add a partition by like so:
WITH rn AS(
SELECT a.Key, ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY a.EntryNum) AS rn
FROM TABLE2 AS a
)
SELECT b.Name, a.Value AS EarliestValue
FROM TABLE2 AS a
INNER JOIN TABLE1 AS b ON b.ID = a.ID
INNER JOIN rn AS rn ON rn.key = a.key
WHERE rn.rn = 1
In your example you skipped the PARTITION BY clause so you just get a number for all values in TABLE2. Instead of a number per ID in ascending order for Value.
Based on your description of the three tables TABLE1, TABLE2 and TABLE3.
I modified a little bit your script. Thank of Dale K remark, I explain in some words the solution : the field TABLE2.Name shown in the first select was wrong, because [name] belongs to TABLE1, so the right syntax for this is TABLE1.name. And in the GROUP BY clause the field TABLE2.Type might be replaced by TABLE1.name to repect aggregation criteria. So the script becomes :
SELECT DISTINCT table1.id, table1.name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY table2.EntryNum)
as row_num, Value FROM table2
WHERE table2.id = table1.id AND table2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO table3
FROM table2
INNER JOIN table1 ON table1.id = table2.id
GROUP BY table1.id, table1.name, table2.entrynum;
Here, you can verify the output with fiddle
You are hugely over-complicating this.
Just partition Table2 and take a row-number, then join that to Table1 and filter on only row-number 1
SELECT
t1.Id,
t1.Name,
EarliestEntry = t2.Value
FROM Table1 t1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.EntryNum)
FROM Table2 t2
WHERE t2.EntryType = 'A'
) t2 ON t2.ID = t1.ID AND t2.rn = 1;
db<>fiddle

SQL Server : self join - retrieve records matching the where clause as well as when no records in alias table

I have a table like below:
Query:
select t1.*
from TABLE2 as t1
left join TABLE2 as t2 on t1.itemcode = t2.itemcode
and t1.warehouseid = '576'
and t1.flag = 'Y'
and t2.warehouseid = '276'
and t2.flag = 'Y';
I have the above query and understand this is not perfect.
For an itemcode, if these conditions are met (t1.warehouseid='576' and t1.flag='Y' and t2.warehouseid='276' and t2.flag='Y') I want to retrieve that from t1.
Also, If there is no entry for an itemcode in t2 (Ex: 456 is not available for warehouseid 276), that also I want to retrieve from t1.
Expecting the following output,
123 576 Y
456 576 Y
What is the correct query for this?
Edit:
To make the post more clear,
Warehouse id 576 is the main element.
For an itemcode, present in both warehouse id (576 , 276) with the flags being same ('Y') , I want to retrieve.
And If the itemcode is not in the other warehouse (276), that also I want to retrieve
For an itemcode, present in both warehouse id (576 , 276) with different flags ('Y' , 'N') , I don't want that.
interpret directly from your 2 conditions in WHERE clause
select *
from TABLE2 t
where warehouseid = 576
and (
exists -- condition 1
(
select *
from TABLE2 x
where x.itemcode = t.itemcode
and x.warehouseid = 276
and x.flag = 'Y'
)
or not exists -- condition 2
(
select *
from TABLE2 x
where x.itemcode = t.itemcode
and x.warehouseid = 276
)
)
Hope this will work for you
select t1.* from TABLE2 as t1
left join TABLE2 as t2
on t1.itemcode=t2.itemcode and t2.warehouseid='276' and t2.flag='Y';
where
t1.warehouseid='576' and t1.flag='Y'
There is another approach using row_number()
with cte as
(select t1.*,
row_number() over(partition by itemcode order by warehouseid desc
) rn
from TABLE2 t1
where not exists ( select 1 from TABLE2 t2 where t1.itemcode=t2.itemcode
and t2.flag='N'
) and t1.warehouseid=576
) select * from cte where rn=1

SQL: Not a simple left/right join problem

I have two tables both with 2 columns (Name[string], value[integer])
I am trying to join the two to display the differences between the sum(value) of each Name. e.g.
Table1(t1):
N1 2
N1 3
N2 4
N3 5
Table(t2):
N1 1
N2 1
N2 1
Result should look like:
N1 4 (5-1)
N2 2 (4-2)
N3 5 (5-0)
We want to assume 0 if N is missing from either one of the tables
I only know how to do the join if the names exist in both table but i do not know how to handle replacing empty results with value of 0 if name is missing in either table.
My current query:
select t1.name, t2.name, t1.Sum-t2.Sum as "Diff"
from (select t1.name, sum(t1.value) as Sum from t1 group by t1.name) t1
inner join
(select t2.name, sum(t2.value) as Sum from t2 group by t2.name) t2
on t1.name = t2.name
The result ignores N3 because N3 is missing from t1.
thank you
You can aggregate both tables using CTEs (Common Table Expressions) and then join them using a FULL OUTER JOIN, as in:
with
l as (
select name, sum(value) as total
from table1
group by name
),
r as (
select name, sum(value) as total
from table2
group by name
)
select
coalesce(l.name, r.name) as name,
coalesce(l.total, 0) - coalesce(r.total, 0) as difference
from l
full outer join r on l.name = r.name
order by coalesce(l.name, r.name)
Use FULL OUTER JOIN and ISNULL
SELECT ISNULL(x.name, y.name) as Name,
ISNULL(x.sumValues, 0) - ISNULL(y.sumValues, 0) as ValueDiff
FROM
(SELECT name, SUM(value) AS sumValues FROM t1 GROUP BY name) x
FULL OUTER JOIN
(SELECT name, SUM(value) AS sumValues FROM t2 GROUP BY name) y
ON x.name = y.name;
A pretty simple method is union all and aggregation:
select name, sum(value)
from ((select name, value
from t1
) union all
(select name, -value
from t2
)
) t
group by name;
The join is a bit messy because it can result in duplicated and missing values, unless done very carefully.
this will work:
input:
create table t1 (name varchar(20),value number);
create table t2 (name varchar(20),value number);
//do inserts
select name1,nvl(sum1-sum2,sum1) from(select sum(t1.value) as sum1,t1.name name1 from t1 group by t1.name)
,(select sum(t2.value) as sum2,t2.name name2 from t2 group by t2.name) where name1=name2(+);
output:
N1 4
N2 2
N3 5

SQL Select row from table with some minimum value from another table

I suppose this is not so hard but I can not get it.
For example I have table T1:
ID
-----
1000
1001
And I have table T2:
ID GROUP DATE
--------------------------
1000 ADSL 2.2.2012
1000 null 3.2.2012
1000 NOC 4.2.2012
1001 NOC 5.2.2012
1001 null 6.2.2012
1001 TV 7.2.2012
I want to select from T1 only the row that has as GROUP value NOC from T2 but only if NOC group is for the minimum DATE value in T2.
So my result in this case would be only 1001 because for its minimum DATE 5.2.2012 Group is NOC!
I do not want any joins and I can not use default values for IDs (where id=1000 or id=1001) because this is just example of some big table.
Important also is that I can not use t1.id = t2.id because in some application where I am using this I can not write the whole SQL expression but only partial. I can only use id.
I tried something like:
select id
from t1
where
id in (select id from t2
where group = 'NOC'
and date in (select min(date) from t2
where id in (select id from t1)
)
)
But this does not work.
I know it seems little confusing but I really can't use where t1.id = t2.id
Thanks
If T2.ID is a foreign key referencing T1.ID, you don't really need the T1 table, because all the IDs could be obtained from T2 only:
SELECT o.ID
FROM T2 AS o
WHERE EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
WHERE o."GROUP" = 'NOC'
But if you insist on involving T1, you just need to modify the above like this:
SELECT *
FROM T1
WHERE ID IN (
SELECT o.ID
FROM T2 AS o
WHERE o."GROUP" = 'NOC'
AND EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
)
Can you do this in multiple steps?
First of all, to get the minimum date per id, you would need:
select id, peoplegroup, min(date)
from t2
group by id
That will give you
1000 ADSL 2.2.2012
1001 NOC 5.2.2012
Call this table t3.
Then do
select id
from t3
where id in (
select id from t1
)
Try this:
select id from t1 where id in
(select id from t2 where group = 'NOC' and date =
(select min(date) from t2 where id = t1.id))