My Table looks like this.
Id | Name | Ref | Date | From
10 | Ant | 100 | 2017-02-02 | David
10 | Ant | 300 | 2016-01-01 | David
2 | Cat | 90 | 2017-09-09 | David
2 | Cat | 500 | 2016-02-03 | David
3 | Bird | 150 | 2017-06-28 | David
This is the result I want.
Id | Name | Ref | Date | From
3 | Bird | 150 | 2017-06-28 | David
2 | Cat | 500 | 2016-02-03 | David
10 | Ant | 300 | 2016-01-01 | David
My target is the highest Ref per Id, ordered by Order Date desc.
Could you please tell me about how to write a sql query using pl/sql.
This kind of requirement (where you need the max or min by one column, grouped by another, but you need all the data from the max or min row) is pretty much what analytic functions are for. I used row_number
- if ties are possible, you need to clarify the assignment (see my Comment under your question), and depending on the details, another analytic function may be more appropriate - perhaps rank()
.
with
my_table ( id, name, ref, dt, frm ) as (
select 10, 'Ant' , 100, date '2017-02-02', 'David' from dual union all
select 10, 'Ant' , 300, date '2016-01-01', 'David' from dual union all
select 2, 'Cat' , 90, date '2017-09-09', 'David' from dual union all
select 2, 'Cat' , 500, date '2016-02-03', 'David' from dual union all
select 3, 'Bird', 150, date '2017-06-28', 'David' from dual
)
-- End of simulated table (for testing purposes only, not part of the solution).
-- SQL query begins BELOW THIS LINE.
select id, name, ref, dt, frm
from (
select id, name, ref, dt, frm,
row_number() over (partition by id order by ref desc, dt desc) as rn
from my_table
)
where rn = 1
order by dt desc
;
ID NAME REF DT FRM
-- ---- --- ---------- -----
3 Bird 150 2017-06-28 David
2 Cat 500 2016-02-03 David
10 Ant 300 2016-01-01 David
You can use this
SELECT
Id
,Name
,Ref
,[Date]
FROM(
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Ref DESC) AS Row#
FROM yourtable
) A WHERE Row# = 1
ORDER BY A.[Date] DESC
Another solution with a self join (Idea came from here: How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL? ):
with
my_table ( id, name, ref, dt, frm ) as (
select 10, 'Ant' , 100, date '2017-02-02', 'David' from dual union all
select 10, 'Ant' , 300, date '2016-01-01', 'David' from dual union all
select 10, 'Ant' , 300, date '2015-01-01', 'David' from dual union all
select 2, 'Cat' , 90, date '2017-09-09', 'David' from dual union all
select 2, 'Cat' , 500, date '2016-02-03', 'David' from dual union all
select 3, 'Bird', 150, date '2017-06-28', 'David' from dual
)
-- End of simulated table (for testing purposes only, not part of the solution).
-- SQL query begins BELOW THIS LINE.
select m1.*
from my_table m1
left join my_table m2
on m1.id = m2.id and (
-- this is basically a comparator: order by ref desc, dt desc
m1.ref < m2.ref or (
m1.ref = m2.ref and
m1.dt < m2.dt
)
) where m2.id is null order by m1.dt desc
;
ID NAME REF DT FRM
---------- ---- ---------- --------- -----
3 Bird 150 28-JUN-17 David
2 Cat 500 03-FEB-16 David
10 Ant 300 01-JAN-16 David
Use the "better than" SQL principal:
select a.Id, a.Name, a.Ref, a.Dt, a.frm
from table_name a
left join table_name b on a.id = b.id and b.ref > a.ref -- b.ref > a.ref would make b.ref "better" that a
where b.id is null -- Now check and make sure there is nothing "better"
group by a.id;
SELECT Id, Name, Max(Ref) as Ref, Min(`Date`) as `Date`
From Forge
Group By Id, Name
Order by Min(`Date`) desc;