join two tables with the max date of the filled field - sql

I have a scenario where i should get VAL from table B by joining A and B with the max date of the filled field, e.g:
A:
F1 F2 F3
-- -- --
1 2 t1
2 3 t2
B:
F1 F2 VAL date
---- ---- --- ----------
1 NULL v10 12/30/2020
1 NULL v11 01/31/2020
NULL 2 v20 02/28/2020
NULL 2 v22 03/30/2020
Desired result:
1 2 t1 v11 01/31/2020
2 3 t2 v22 03/30/2020
Thank you in advance.

You can use MAX() and FIRST_VALUE() window functions:
SELECT DISTINCT A.f1, A.f2, A.f3,
FIRST_VALUE(B.VAL) OVER (PARTITION BY COALESCE(B.f1, B.f2) ORDER BY "date" DESC) VAL,
MAX(B."date") OVER (PARTITION BY COALESCE(B.f1, B.f2)) "date"
FROM A INNER JOIN B
ON A.f1 = COALESCE(B.f1, B.f2)
See the demo.

select A.* , maxB.* , B.VAL
from
A
join B
on A.f1 = B.F1
or A.f2 = B.F2
join
(
select
F1, F2 ,VAL , max(date) date
from
B
group by F1,F2
) maxB
on
B.date = maxB.date
and (
maxB.f1 = B.F1
or maxB.f2 = B.F2
)

Try with this
SELECT
R.F1, R.F2, R.F3, R.VAL,R.date
FROM
(
SELECT
A.F1, A.F2, A.F3,
B.VAL,B.date, RANK() OVER(PARTITION BY A.F1,A.F2,A.F3 ORDER BY B.date DESC) as Rank
FROM A
JOIN B ON B.F1 = A.F1 OR B.F2 = A.F1
) R
WHERE
R.Rank = 1

You can use ROW_NUMBER() analytic function within the subquery
WITH AB AS
(
SELECT A.f1, A.f2, B.val, B."date",
ROW_NUMBER() OVER (PARTITION BY NVL(B.f1, B.f2) ORDER BY "date" DESC) AS rn
FROM A
JOIN B
ON A.f1=B.f1 OR A.f1=B.f2
)
SELECT f1,f2,val,"date"
FROM AB
WHERE rn = 1
If ties(equal values) for the date values is the case and all should be included within the result set, then replace ROW_NUMBER() with DENSE_RANK() function.
Demo

Related

getting other rank value than 1 in oracle in SQL query

There is 1 SQL query when i used like below-
select * from
(select a.id, a.nm, b.pd, b.date, rank() over(partition by a.id order by b.date desc) rnk
from tab1 a, tab2 b where a.id = b.id)
where rnk =1
then getting output like below-
id nm pd date rnk
-- --- --- ---------- ---
11 abc a15 11/04/2022 1
11 abc a15 11/04/2022 1
14 gef a23 11/04/2022 1
14 gef a23 10/04/2022 12
19 lfb a37 11/04/2022 1
19 lfb a37 08/04/2022 21
But I want only one value to be select as order by latest date. Could you please help me on this to resolve it to select one value for 1 id like below-
id nm pd date rnk
-- --- --- ---------- ---
11 abc a15 11/04/2022 1
14 gef a23 11/04/2022 1
19 lfb a37 11/04/2022 1
You need to specify a second column in the order by of the RANK(), so that there are no duplicate pairs of values (e.g. b.id). I've also normalised the JOIN operation.
select * from
(select
a.id,
a.nm,
b.pd,
b.date,
rank() over (
partition by a.id
order by b.[date] desc, b.id asc
) as rnk
from tab1 a
join tab2 b on a.id = b.id
) s
where rnk = 1;
Have you tried like this because sometimes rank() function doesn't work just outside main SQL. Try it, hope it will work.
SELECT id,
nm,
pd, date
FROM
(SELECT *
FROM
(SELECT a.id,
a.nm,
b.pd,
b.date ,
rank() over(PARTITION BY a.id
ORDER BY b.date DESC) rnk
FROM tab1 a,
tab2 b
WHERE a.id = b.id))
WHERE rnk =1

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)

sql server merge rows and get latest value

I have a table like this,
Id A B C D touchedwhen
1 NULL yes NULL yes 2015-02-26 14:10:01.870
1 NULL NULL no no 2015-02-26 14:10:40.370
and need to merge them in to one row like this,
Id A B C D touchedwhen
1 NULL yes no no 2015-02-26 14:10:40.370.
Note : if value is present in both rows take the latest one by date..
Tried this query:
select id,
max(a),
max(b),
max(c),
max(d), -- data in both rows hence take the latest
max(touchedwhen)
from
[dbo].[Table_1]
group by id;
If you were just looking for the last value per id, you could use:
last_value(a) over(
partition by id
order by touchedwhen desc
rows between unbounded preceding and unbounded following) as a
But you're looking for the last value per id that is not null. The only thing I can come up with is a four-part join, with each subquery calculating the latest non-null value for a, b, c, d:
select ids.id
, a.a
, b.b
, c.c
, d.d
, ids.tw
from (
select id
, max(touchedwhen) as tw
from YourTable
group by
id
) ids
left join
(
select row_number() over (
partition by id
order by touchedwhen desc) rn
, a
, id
from YourTable
where a is not null
) a
on a.id = ids.id
and a.rn = 1
left join
(
select row_number() over (
partition by id
order by touchedwhen desc) rn
, b
, id
from YourTable
where b is not null
) b
on b.id = ids.id
and b.rn = 1
left join
(
select row_number() over (
partition by id
order by touchedwhen desc) rn
, c
, id
from YourTable
where c is not null
) c
on c.id = ids.id
and c.rn = 1
left join
(
select row_number() over (
partition by id
order by touchedwhen desc) rn
, d
, id
from YourTable
where d is not null
) d
on d.id = ids.id
and d.rn = 1
-- Get all latest values in one row for each primary key
WITH CTE(row_num, Id, A, B, C, D, touchedwhen) AS (
SELECT ROW_NUMBER() OVER(PARTITION BY Id ORDER BY touchedwhen DESC), Id, A, B, C, D FROM Table_1)
UPDATE CTE SET
A = (SELECT TOP 1 t.A FROM Table_1 t WHERE t.A IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC),
B = (SELECT TOP 1 t.B FROM Table_1 t WHERE t.B IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC),
C = (SELECT TOP 1 t.C FROM Table_1 t WHERE t.C IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC),
D = (SELECT TOP 1 t.D FROM Table_1 t WHERE t.D IS NOT NULL AND t.Id = CTE.Id ORDER BY t.touchedwhen DESC)
WHERE row_num = 1
-- Delete extra rows per primary key after copying latest values to one row
WITH CTE(row_num) AS (
SELECT ROW_NUMBER() OVER(PARTITION BY Id ORDER BY touchedwhen DESC) FROM Table_1)
DELETE FROM CTE WHERE row_num > 1

Query for Retrieving

I need to build a query which should give me below mentioned output.
My table structure Required output
Depth | Name Level A | Level B | Level C
1 |A A | B | C
2 |B A1 | B1 | C1
3 |C A1 | B1 | C2
1 |A1 A1 | B2 | C3
2 |B1 A1 | B2 | C4
3 |C1
3 |C2
2 |B2
3 |C3
3 |C4
Thanks in advance
Given your particular data, you can come close with:
select a.name as levela, b.name as levelb, c.name as levelc
from (select name, row_number() over (order by id) as seqnum from table where depth = 1
) a full outer join
(select name, row_number() over (order by id) as seqnum from table where depth = 2
) b full outer join
on b.seqnum = a.seqnum
(select name, row_number() over (order by id) as seqnum from table where depth = 3
) c
on c.seqnum = coalesce(a.seqnum, b.seqnum);
This inserts NULLs instead of repeating the final values for the three columns. If you want the final values, this should work:
select coalesce(a.name, maxes.a) as levela,
coalesce(b.name, maxes.b) as levelb,
coalesce(c.name, maxes.c) as levelc
from (select name, row_number() over (order by id) as seqnum from table where depth = 1
) a full outer join
(select name, row_number() over (order by id) as seqnum from table where depth = 2
) b full outer join
on b.seqnum = a.seqnum
(select name, row_number() over (order by id) as seqnum from table where depth = 3
) c
on c.seqnum = coalesce(a.seqnum, b.seqnum) cross join
(select max(case when depth = 1 and id = maxid then name end) as max_a,
max(case when depth = 2 and id = maxid then name end) as max_b,
max(case when depth = 3 and id = maxid then name end) as max_c
from (select t.*,
max(id) over (partition by depth) as maxid
from t
) t
) maxes
Since the relationship between levelA,levelB,levelC is not clear.I have assumed you want to return the max(name) in case the corresponding value is not available.The Sql Fiddle here.You can replace
order by my_table.name into order by unique_seq_column and also max(name) name by the name value in max(unique_seq_column) if it suits your requirement
--Get the max count and max name for each level
with cnt_max1 as (select max(name) name ,count(1) cnt from my_table where depth=1)
,cnt_max2 as (select max(name) name ,count(1) cnt from my_table where depth=2)
,cnt_max3 as (select max(name) name ,count(1) cnt from my_table where depth=3)
--find out the total rows required
,greatest_cnt as (select greatest(cnt_max1.cnt,cnt_max2.cnt,cnt_max3.cnt) cnt from cnt_max1,cnt_max2,cnt_max3)
--Establish relationship between levelA,levelB,levelC using sublevel column
,level_A as (select * from (select rownum sublevel, my_table.name as levela from my_table where depth=1 order by my_table.name)
union
select level+cnt_max1.cnt sublevel,cnt_max1.name levela
from cnt_max1,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max1.cnt))
,level_B as (select * from (select rownum sublevel, my_table.name as levelb from my_table where depth=2 order by my_table.name)
union
select level+cnt_max2.cnt sublevel,cnt_max2.name levelb
from cnt_max2,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max2.cnt))
,level_C as (select * from (select rownum sublevel, my_table.name as levelc from my_table where depth=3 order by my_table.name)
union
select level+cnt_max3.cnt sublevel,cnt_max3.name levelc
from cnt_max3,greatest_cnt connect by level <=(greatest_cnt.cnt - cnt_max3.cnt))
--Display the data
select levela,levelb,levelc
from level_A join level_b
on level_A.sublevel=level_B.sublevel
join level_c on level_C.sublevel=level_b.sublevel

SQL Group By and Agregate a column and its correspondent value in another column

f1 f2 f3
----------------
10 20 30
10 15 50
11 12 25
11 79 13
Grouping by f1, how to get the max f2 and its correspondent f3?
f1 f2 f3
----------------
10 20 30
11 79 13
you can do it this way
SELECT t1.f1
, t.f2
, t1.f3
FROM tbl t1
cross apply (SELECT max(f2) f2
FROM tbl
GROUP BY f1) t
WHERE t1.f2=t.f2
You may use a cte with a row_number, partition by f1 and select only first row of each (ordered by f2 desc, which will give you the max f2)
with cte as (select
f1,
f2,
f3,
row_number() over(partition by f1 order by f2 desc) rn
from t)
select
f1,
f2,
f3
from cte
where rn = 1
see SqlFiddle
You want to use row_number():
select t.*
from (select t.*, row_number() over (partition by f1 order by f2 desc) as seqnum
from table t
) t
where seqnum = 1;
You can also use this one (without RN):
SELECT DISTINCT a.f1,x.f2,x.f3
FROM YourTable a
CROSS APPLY (SELECT TOP 1 f2,f3
FROM YourTable b
WHERE a.f1 = b.f1 ORDER BY b.f2 DESC) x