SQL, left join table, How to keep only one value? - sql

I would like to join two tables.
The table 1 is like this:
id Date
a 01.01.2021
a 02.01.2021
a 03.01.2021
b 01.01.2021
b 02.01.2021
b 03.01.2021
c 01.01.2021
c 02.01.2021
c 03.01.2021
The table 2 is like this:
id value
a 12
a 8
b 50
As final result, I would like to have a table like this:
for an existing id from table 2, the sum of the value of the same id should be stored in the last date in the table 1.
id Date Value_FINAL
a 01.01.2021 0
a 02.01.2021 0
a 03.01.2021 20
b 01.01.2021 0
b 02.01.2021 0
b 03.01.2021 50
c 01.01.2021 0
c 02.01.2021 0
c 03.01.2021 0
I tried to use left join to join these two tables at first,
with t3 as ( select id, sum(value) Value_FINAL from t2 group by id)
select t1.*, t3.value_FINAL from t1 left join t3 on t1.id = t3.id;
After this, I can get this:
id Date Value_FINAL
a 01.01.2021 20
a 02.01.2021 20
a 03.01.2021 20
b 01.01.2021 50
b 02.01.2021 50
b 03.01.2021 50
c 01.01.2021 0
c 02.01.2021 0
c 03.01.2021 0
But, it is not I want. can someone help with this? How can I keep the value only in the last Date in the column 'value_FINAL'
I am also thinking about to use last_value(value) over (partition by id order by date). But I need to create an extra table or column.
maybe someone has a good idea how to deal this problem?

A join based alternative
select a.*, coalesce(c.value,0)
from t1 a
left join (select id, max(date) date from t1 group by id) b on a.id = b.id and a.date = b.date
left join (select id, sum(value) value from t2 group by id) c on b.id = c.id

Use row_number(). One method is:
select t1.*,
(case when row_number() over (partition by id order by date desc) = 1
then (select coalesce(sum(t2.value), 0) from table2 t2 where t2.id = t1.id)
else 0
end) as value_final
from table1 t1;

You can use ROW_NUMBER to identify the row where you want to place the total value.
For example:
select
t1.id, t1.date, b.total,
case when
row_number() over(partition by t1.id order by t1.date desc) = 1
then b.total
else 0 end as value_final
from t1
left join (select id, sum(value) as total from t2 group by id) b
on b.id = t1.id

Related

How to select rows by max value from another column in Oracle

I have two datasets in Oracle Table1 and Table2.
When I run this:
SELECT A.ID, B.NUM_X
FROM TABLE1 A
LEFT JOIN TABLE2 B ON A.ID=B.ID
WHERE B.BOOK = 1
It returns this.
ID NUM_X
1 10
1 5
1 9
2 2
2 1
3 20
3 11
What I want are the DISTINCT ID where NUM_X is the MAX value, something like this:
ID NUM_x
1 10
2 2
3 20
You can use aggregation:
SELECT A.ID, MAX(B.NUM_X)
FROM TABLE1 A LEFT JOIN
TABLE2 B
ON A.ID = B.ID
WHERE B.BOOK = 1
GROUP BY A.ID;
If you wanted additional columns, I would recommend window functions:
SELECT A.ID, MAX(B.NUM_X)
FROM TABLE1 A LEFT JOIN
(SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY NUM_X DESC) as seqnum
FROM TABLE2 B
) B
ON A.ID = B.ID AND B.seqnum = 1
WHERE B.BOOK = 1
GROUP BY A.ID;

SQL get closest value by date

can't wrap my mind around the next problem
I have a table with historical data TableA:
uniq_id item_id item_clust date
11111 1 a 2020-02-12
11112 1 a 2020-01-13
11113 1 b 2020-02-01
11114 2 b 2020-01-01
I also have a table with historical data for clusters TableB:
item_id item_clust item_pos date
1 a 1 2020-01-01
1 a 2 2020-02-01
1 a 3 2020-03-01
1 b 1 2020-01-10
I would like to receive the latest position for every item_id + item_clust for date based on dates in TableB
If no rows found, I would like to insert item_pos = 0
Desired result:
uniq_id item_id item_clust date item_pos
11111 1 a 2020-02-12 2
11112 1 a 2020-01-13 1
11113 1 b 2020-02-01 1
11114 2 b 2020-01-01 0
So, for item 1 in cluster a on 2020-02-12 the latest position is at 2020-02-01 = 2.
This looks like a left join:
select a.*, coalesce(b.item_pos, 0) as item_pos
from a left join
(select distinct on (b.item_id, b.item_clust) b.*
from b
order by b.item_id, b.item_clust, b.date desc
) b
using (item_id, item_clust);
Or a lateral join:
select a.*, coalesce(b.item_pos, 0) as item_pos
from a left join lateral
(select b.*
from b
where b.item_id = a.item_id and
b.item_clust = a.item_clust
order by b.date desc
limit 1
) b
on true; -- always do the left join even when there are no matches
EDIT:
If you want the most recent position "as of" the date in A, then use the lateral join:
select a.*, coalesce(b.item_pos, 0) as item_pos
from a left join lateral
(select b.*
from b
where b.item_id = a.item_id and
b.item_clust = a.item_clust and
b.date <= a.date
order by b.date desc
limit 1
) b
on true; -- always do the left join even when there are no matches

Need Full Outer Join without having Cross Join

Need to join two table without having cross join between them.
The join condition need to be made on Tabl.month = Tab2.month
Input
Table1 Table2
Month ID Month ID
1 a 1 a
1 b 1 b
1 c 2 g
2 d 3 i
2 e 3 j
3 f 3 k
Output:
Month_Tab1 ID_Tab1 Month_Tab2 ID_Tab2
1 a 1 a
1 b 1 b
1 c Null Null
2 d 2 g
2 e Null Null
3 f 3 i
Null Null 3 j
Null Null 3 k
The above o/p is required, without cross join, have tried full outer but cross join is happening as the ID is duplicate in both Tables. Left/Right join also cannt be applicable as either of the table might have larger set of ID's.
You want a full join, but with row_number() to identify the matches:
select t1.month month_tab1, t1.id id_tab1, t2.month month_tab2, t2.id id_tab2
from (
select t.*, row_number() over(partition by month order by id) rn from table1 t
) t1
full join (
select t.*, row_number() over(partition by month order by id) rn from table2 t) t2
on t2.month = t1.month and t2.rn = t1.rn
You can use a full outer join:
select
a.month,
a.id,
b.month,
b.id
from (
select month, id,
row_number() over(partition by month order by id) as n
from table1
) a
full outer join (
select month, id,
row_number() over(partition by month order by id) as n
from table2
) b on b.month = a.month and b.n = a.n
order by coalesce(a.month, b.month), coalesce(a.n, b.n)

Select Missing date only if it is Maximum from a table in Oracle

I have 2 tables. If table 1 has dates greater than table 2 only those record has should be populated in Output.
Table 1:
ID Category Date
1 A 3/2/1990
1 A 3/5/2013
1 C 4/3/1979
2 D 4/3/1970
2 D 5/6/2016
3 E 8/8/2016
Table 2:
ID Category Date
1 A 3/2/1990
1 C 4/3/1979
1 C 4/3/1982
1 D 4/3/1982
2 D 5/6/2016
The expected Output is
ID Category Date
1 A 3/5/2013
3 E 8/8/2016
I tried the below query and its giving me incorrect results.
select a.id,a.category,a,Date from table1 a where
a.Date > (select Max(b.Date) from table2 b where a.id=b.id and a.category =b.catgory group by b.id,b.category)
SQL Fiddle Demo
WITH cte AS (
SELECT ID, Category, MAX(Date) as mdate
FROM Table2
GROUP BY ID, Category
)
SELECT T1.* --, T2.*
FROM Table1 as T1
LEFT JOIN cte as T2
ON T1.ID = T2.ID
AND T1.Category = T2.Category
WHERE T1.Date > T2.mdate
OR T2.mdate is NULL
OUTPUT
SELECT T1.*
FROM Table1 AS T1 INNER JOIN Table2 AS T2
ON T1.ID = T2.ID
WHERE T1.Date > T2.mdate;
As per the required output, you need to use left outer join
SELECT T1.*
FROM table1 T1
LEFT OUTER JOIN (
SELECT ID
,category
,MAX(Date) mdate
FROM Table2
GROUP BY ID
,category
) T2 ON (
T1.ID = T2.ID
AND T1.category = T2.category
)
WHERE T1.date > nvl(T2.mdate, '01/01/1900');
Filtering Table2:
SELECT ID, Category,MAX(Date) as Date
FROM Table2
GROUP BY ID,Category;
| ID | Category | Date |
|----|----------|-------------------------|
| 1 | A | March, 02 1990 00:00:00 |
| 1 | C | April, 03 1982 00:00:00 |
| 1 | D | April, 03 1982 00:00:00 |
| 2 | D | May, 06 2016 00:00:00 |
Now using this to create a left join with Table1:
SELECT t1.*
FROM Table1 t1 LEFT JOIN
(SELECT ID, Category,MAX(Date) as Date
FROM Table2
GROUP BY ID,Category) AS t2part
ON t1.ID = t2part.ID
AND t1.Category = t2part.Category
WHERE t1.Date > t2part.Date;
| ID | Category | Date |
|----|----------|-------------------------|
| 1 | A | March, 05 2013 00:00:00 |
Please note that the row with ID=3, category=E wasn't found due to not matching neither ID or Category in the JOIN.
As good practice if the entities should interact there must be some sort of normalization applied so we could make best use of joins through indexes.
fiddle with your provided data and queries.

SQL: How do I combine tables on a single but non-unique identifier?

I have two tables:
TABLE 1
ID Value ValueFromTable2
1 A NULL
1 B NULL
1 C NULL
1 D NULL
2 E NULL
2 F NULL
TABLE 2
ID Value
1 A1
1 A2
1 A3
2 BOB
2 JIM
I would like to update TABLE 1 with the values of TABLE 2 such that the following rows would result:
TABLE 1
ID Value ValueFromTable2
1 A A1
1 B A2
1 C A3
1 D NULL
2 E BOB
2 F JIM
Order it not terribly important. That is, I'm not concerned that A be paired with A1 or that B be paired with A2. I just need a full set of data from the Value column in Table 2 to be available from Table 1.
Please advise!
You need a key for joining them. The implicit key is the ordering. You can add that in explicitly, using row_number():
select coalesce(t1.id, t2.id) as id,
t1.value, t2.value
from (select t1.*, row_number() over (partition by id order by (select NULL)) as seqnum
from table1 t1
) t1 full outer join
(select t2.*, row_number() over (partition by id order by (select NULL)) as seqnum
from table2 t2
) t2
on t1.id = t2.id and t1.seqnum = t2.seqnum;
By using full outer join, all values will appear, regardless of which is the longer list.