Oracle select query to filter rows - sql

Say I have a table with the following values
id (PK) a_num a_code effect_dt expire_dt
32 1234 abcd 01/01/2015 05/30/2015
9 1234 abcd 06/01/2015 12/31/2015
5 1234 efgh 01/01/2015 05/30/2015
14 1234 efgh 06/01/2015 12/31/2015
How can I select just one record from a_num,a_code pair. Either Id's 1,3 or 2,4? There may be scenarios where there are more than 2 records for a a_num,a_code pair.
UPDATE - ID will not necessarily always be in order, it is just a primary key.

This will give you rows 1 and 3
Select * from (
Select * , Row_number() Over(Partition by a_num, a_code order by id) r_num from Your_Table ) result
Where r_num = 1
Just use DESC in order by and you will get rows 2 and 4
Select * from (
Select * , Row_number() Over(Partition by a_num, a_code order by id desc) r_num from Your_Table ) result
Where r_num = 1

One way would be to use the row_number window function:
SELECT id, a_num, a_code, effect_dt, expire_dt
FROM (SELECT id, a_num, a_code, effect_dt, expire_dt,
ROW_NUMBER() OVER (PARTITION BY a_num, a_code
ORDER BY 1) AS rn
FROM mytable) t
WHERE rn = 1

Related

List the most up-to-date product of each category,postqresql queries

user_id product_id category_id date_added date_update
1 2 1 2.3.2021 null
1 3 1 2.3.2020 2.4.2023
1 4 2 2.3.2020 null
1 5 2 2.3.2020 2.4.2023
2 5 2 2.3.2020 2.4.2023
2 4 1 2.3.2020 null
List the most up-to-date product of each category
You can use row_number()
select * from
(
select *,row_number() over(parition by userid,category_id order by date_update) as rn
from tablename
)A where rn=1
OR you can also use distinct on
select distinct on (user_id,category_id) *
FROM tablename
ORDER BY user_id,category_id, date_update
List the most up-to-date product of each category
You can use distinct on. Let me assume that if the update date is null, then you want the creation date:
select distinct on (category_id) t.*
from t
order by category_id, coalesce(date_update, date_added) desc;
If you wanted this per user/category combination, the logic would be:
select distinct on (user_id, category_id) t.*
from t
order by user_id, category_id, coalesce(date_update, date_added) desc;
Using Window function
select u_id,c_id, p_id, coalesce (date_update, date_added) as date ,
rank () over (partition by u_id, c_id order by coalesce (date_update, date_added) desc) as r
from inventory
) t where r = 1

DB2 Toad SQL - Group by Certain Columns using Max Command

I am having some trouble with the below query. I do understand I need to group by ID and Category, but I only want to group by ID while keeping the rest of the columns based on Rank being max. Is there a way to only group by certain columns?
select ID, Category, max(rank)
from schema.table1
group by ID
Input:
ID Category Rank
111 3 4
111 1 5
123 5 3
124 7 2
Current Output
ID Category Rank
111 3 4
111 9 1
123 5 3
124 7 2
Desired Output
ID Category Rank
111 1 5
123 5 3
124 7 2
You can use:
select *
from table1
where (id, rank) in (select id, max(rank) from table1 group by id)
Result:
ID CATEGORY RANK
---- --------- ----
111 1 5
123 5 3
124 7 2
Or you can use the ROW_NUMBER() window function. For example:
select *
from (
select *,
row_number() over(partition by id order by rank desc) as rn
from table1
) x
where rn = 1
See running example at db<>fiddle.
You can try using - row_number()
select * from
(
select ID, Category,rank, row_number() over(partition by id order by rank desc) as rn
from schema.table1
)A where rn=1

rank a column with out ordering in oracle

I have the data as below, When I apply dense_rank by ordering id column, I am getting rank according to the order of integers but I need to rank as the records are displayed when run a query:
Data from query:
Rid id
8100 161
8101 2
8102 2
8103 2
8104 156
When I apply dense_rank over order by id then I am getting
Rid id rank
8100 161 3
8101 2 1
8102 2 1
8103 2 1
8104 156 2
But my requirement is to get in below way:
Rid id rank
8100 161 1
8101 2 2
8102 2 2
8103 2 2
8104 156 3
Used row_number as well but the result is not as expected, not sure what option would be the better way.
Any help is appreciated.
Thanks
Edit------------------------------
Query used
Select rid, id,dense_rank() over (order by id) row_num
from table
I have adjusted solution from here: DENSE_RANK according to particular order for your need.
I am not sure if I should mark this as duplicate because on this link above there is no ORACLE tag. If more experience members think I should please do comment and I will do so and delete this answer.
Here is the adjusted code and demo:
SELECT t2.rid
, t2.id
, DENSE_RANK() OVER (ORDER BY t2.max_rid)
FROM (
SELECT MAX(t1.rid) OVER (PARTITION BY t1.grupa) AS max_rid
, t1.rid
, t1.id
FROM (
SELECT rid
, id
,ROW_NUMBER() OVER (ORDER BY rid) - ROW_NUMBER() OVER (PARTITION BY id ORDER BY rid) AS grupa
FROM test_table) t1 ) t2
ORDER BY rid
DEMO
You can use sum() aggregation containing (order by rid) after getting the values from lag() analytic function within the first query
with tab( rid,id ) as
(
select 8100,161 from dual union all
select 8101,2 from dual union all
select 8102,2 from dual union all
select 8103,2 from dual union all
select 8104,156 from dual
), t2 as
(
select t.*, lag(id,1,0) over (order by rid) lg
from tab t
)
select rid, id, sum(case when lg!=id then 1 else 0 end) over (order by rid) as row_num
from t2
Demo

How can I select an ID each month with the highest Mark

I am fairly new to SQL. My table is
id mark datetimes
------|-----|------------
1001 | 10 | 2011-12-20
1002 | 11 | 2012-01-10
1005 | 12 | 2012-01-10
1003 | 10 | 2012-01-10
1004 | 11 | 2018-10-10
1006 | 12 | 2018-10-19
1007 | 13 | 2018-03-12
1008 | 15 | 2018-03-13
I need to select an ID with the highest mark at the end of each month (Year also matters) and ID can be repeated
My desired output would be
id mark
-----|----
1001 | 10
1005 | 12
1006 | 12
1008 | 15
So far I've Only able to get the highest value in each month
Select Max(Mark)'HighestMark'
From StudentMark
Group BY Year(datetimes), Month(datetimes)
When I tried to
Select Max(Mark)'HighestMark', ID
From StudentMark
Group BY Year(datetimes), Month(datetimes), ID
I get
Id HighestMark
----------- ------------
1001 10
1002 11
1003 12
1004 10
1005 11
1006 12
1007 13
1008 15
You can try like following.
Using ROW_NUMBER()
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY YEAR(DATETIMES)
,MONTH(DATETIMES) ORDER BY MARK DESC) AS RN
FROM [MY_TABLE]
)T WHERE RN=1
Using WITH TIES
SELECT TOP 1 WITH TIES ID, mark AS HighestMarks
FROM [MY_TABLE]
ORDER BY ROW_NUMBER() OVER (PARTITION BY YEAR(datetimes)
,MONTH(datetimes) ORDER BY mark DESC)
Example:
WITH MY AS
(
SELECT
* FROM (VALUES
(1001 , 10 , '2011-12-20'),
(1002 , 11 , '2012-01-10'),
(1005 , 12 , '2012-01-10'),
(1003 , 10 , '2012-01-10'),
(1004 , 11 , '2018-10-10'),
(1006 , 12 , '2018-10-19'),
(1007 , 13 , '2018-03-12'),
(1008 , 15 , '2018-03-13')
) T( id , mark , datetimes)
)
SELECT ID,Mark as HighestMark FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY YEAR(DATETIMES),MONTH(DATETIMES) ORDER BY MARK DESC) AS RN
FROM MY
)T WHERE RN=1
Output:
ID HighestMark
1001 10
1005 12
1008 15
1006 12
I don't see a way of doing this in a single query. But we can easily enough use one subquery to find the final mark in the month for each student, and another to find the student with the highest final mark.
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ID, CONVERT(varchar(7), datetimes, 126)
ORDER BY datetimes DESC) rn
FROM StudentMark
)
SELECT ID, Mark AS HighestMark
FROM
(
SELECT *,
RANK() OVER (PARTITION BY CONVERT(varchar(7), datetimes, 126)
ORDER BY Mark DESC) rk
FROM cte
WHERE rn = 1
) t
WHERE rk = 1
ORDER BY ID;
Demo
In below query you have included ID column for Group By, because of this, it is considering all data for all ID.
Select Max(Mark)'HighestMark', ID From StudentMark Group BY Year(datetimes), Month(datetimes), ID
Remove ID column from this script and try again.
Use RANK in case there are more than 1 student having the same highest mark.
select id, mark
from
(select *,
rank() over( partition by convert(char(7), datetimes, 111) order by mark desc) seqnum
from studentMark ) t
where seqnum = 1
this should work:
select s.ID, t.Mark, t.[Month year] from Studentmark s
inner join (
Select
Max(Mark)'HighestMark'
,cast(Year(datetimes) as varchar(10)) +
cast(Month(datetimes) as varchar(10)) [month year]
From StudentMark
Group BY cast(Year(datetimes) as varchar(10))
+ cast(Month(datetimes) as varchar(10))) t on t.HighestMark = s.mark and
t.[month year] = cast(Year(s.datetimes) as varchar(10)) + cast(Month(s.datetimes) as varchar(10))
If for some reason you abhor subqueries, you can actually do this as:
select distinct
first_value(id) over (partition by year(datetimes), month(datetime) order by mark desc) as id
max(mark) over (partition by year(datetimes), month(datetime))
from StudentMark;
Or:
select top (1) with ties id, mark
from StudentMark
order by row_number() over (partition by year(datetimes), month(datetime) order by mark desc);
In this case, you can get all students in the event of ties by using rank() or dense_rank() instead of row_number().

selecting set of second lowest values

I have two columns of interest ID and Deadline:
ID Deadline (DD/MM/YYYY)
1 01/01/2017
1 05/01/2017
1 04/01/2017
2 02/01/2017
2 03/01/2017
2 06/02/2017
2 08/03/2017
Each ID can have multiple (n) deadlines. I need to select all rows where the Deadline is second lowest for each individual ID.
Desired output:
ID Deadline (DD/MM/YYYY)
1 04/01/2017
2 03/01/2017
Selecting minimum can be done by:
select min(deadline) from XXX group by ID
but I am lost with "middle" values. I am using Rpostgresql, but any idea helps as well.
Thanks for your help
One way is to use ROW_NUMBER() window function
SELECT id, deadline
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY deadline) rn
FROM xxx
) q
WHERE rn = 2 -- get only second lowest ones
or with LATERAL
SELECT t.*
FROM (
SELECT DISTINCT id FROM xxx
) i JOIN LATERAL (
SELECT *
FROM xxx
WHERE id = i.id
ORDER BY deadline
OFFSET 1 LIMIT 1
) t ON (TRUE)
Output:
id | deadline
----+------------
1 | 2017-04-01
2 | 2017-03-01
Here is a dbfiddle demo
Using ROW_NUMBER() after taking distinct records will eliminate the chance of getting the lowest date instead of second lowest if there are duplicate records.
select ID,Deadline
from (
select ID,
Deadline,
ROW_NUMBER() over(partition by ID order by Deadline) RowNum
from (select distinct ID, Deadline from SourceTable) T
) Tbl
where RowNum = 2