SQL get entries where on attribute is max - sql

I have the following dataset:
id
id_rev
time
1
1
08.01.2022
1
0
31.02.2021
2
2
28.01.2017
2
1
25.07.2021
2
0
25.07.2021
I am looking for a SQL query that can return an entry per id but only the one where the id_rev is maximum. So in this case it should return these two rows:
(id=1, id_rev=1,time)
(id=2, id_rev=2, time)

One canonical approach uses ROW_NUMBER:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY id_rev DESC) rn
FROM yourTable t
)
SELECT id, id_rev, time
FROM cte
WHERE rn = 1
ORDER BY id;
Another approach would be to use exists logic:
SELECT id, id_rev, time
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.id = t1.id AND t2.id_rev > t1.id_rev
);

#result =
SELECT
*,
RANK()
OVER (PARTITION BY id ORDER BY id_rev DESC) AS Rank
FROM dataset ORDER BY Rank;
#result =
SELECT *
FROM #result
WHERE Rank = 1;

Related

Random records in Oracle table based on conditions

I have a Oracle table with the following columns
Table Structure
In a query I need to return all the records with CPER>=40 which is trivial. However, apart from CPER>=40 I need to list 5 random records for each CPID.
I have attached a sample list of records. However, in my table I have around 50,000 records.
Appreciate if you can help.
Oracle solution:
with CTE as
(
select t1.*,
row_number() over(order by DBMS_RANDOM.VALUE) as rn -- random order assigned
from MyTable t1
where CPID <40
)
select *
from CTE
where rn <=5 -- pick 5 at random
union all
select t2.*, null
from my_table t2
where CPID >= 40
SQL Server:
with CTE as
(
select t1.*,
row_number() over(order by newid()) as rn -- random order assigned
from MyTable t1
where CPID <40
)
select *
from CTE
where rn <=5 -- pick 5 at random
union all
select t2.*, null
from my_table t2
where CPID >= 40
How about something like this...
SELECT *
FROM (SELECT CID,
CVAL,
CPID,
CPER,
Row_number() OVER (partition BY CPID ORDER BY CPID ASC ) AS RN
FROM Table) tmp
WHERE CPER>=40 OR pids <= 5
However, this is not random.
Assuming that you want five additional random records, you can do:
select t.*
from (select t.*,
row_number() over (partition by cpid,
(case when cper >= 40 then 1 else 2 end)
order by dbms_random.value
) as seqnum
from t
) t
where seqnum <= 5 or cper >= 40;
The row_number() is enumerating the rows for each cpid in two groups -- based on the cper value. The outer where is taking all cper values in the range you want as well as five from the other group.

penultimate date for each record

I'm struggling with creation of select which shows me penultimate date for each record in my DB.
For example:
id date
1 01.01.2018
1 05.01.2018
1 06.02.2018
2 01.06.2018
2 03.06.2018
3 12.12.2017
Out of this record I need to write select, which shows me following:
ID max_date penultimate
1 06.02.2018 05.01.2018
2 03.06.2018 01.06.2018
3 12.12.2017 NULL
Any idea how to do it? many thanks in advance
Use conditional aggregation and the ANSI-standard row_number() or dense_rank() functions:
select id,
max(date) as max_date,
max(case when seqnum = 2 then date end) as penultimate_date
from (select t.*,
dense_rank() over (partition by id order by date desc) as seqnum
from t
) t
where seqnum in (1, 2)
group by id;
Use row_number() if the dates can be the same in the event of ties.
Use GROUP BY to get the MAX and a correlated subquery with another MAX but this time lower than the former.
SELECT
T.id,
MAX(T.date) max_date,
(
SELECT
MAX(N.date)
FROM
YourTable N
WHERE
N.id = T.id AND
N.date < MAX(T.date)
) penultimate
FROM
YourTable T
GROUP BY
T.id
Just an opitimized query:
;WITH cte AS
(
SELECT id AS ID
,[date] AS max_date
,LEAD ([date], 1, 0) OVER (PARTITION BY id ORDER BY [date] DESC) AS penultimate
,ROW_NUMBER() OVER(PARTITION BY id ORDER BY [date] DESC) AS RN
FROM Table3
)
SELECT ID,max_date,penultimate
FROM cte
WHERE RN=1
SQL Fiddle
I wrote in this way,
SELECT ID
,max(StartDate) MaxDate
,(
SELECT StartDate
FROM YourTable t2
WHERE t2.id = t1.id
ORDER BY StartDate DESC OFFSET 1 ROWS FETCH NEXT 1 ROW ONLY
) penultimate
FROM YourTable t1
GROUP BY id

Get MAX ID from multiple records in table where ID2 is the same and where Value 3<>0

In the above screenshot I need to get the subplanid where MAX(ID) in that group of subplanid does not have a formularymixtype of 0
I think this does what you want:
select t.*
from (select t.*,
row_number() over (partition by subplanid order by id desc) as seqnum
from t
) t
where seqnum = 1 and formularymixtype <> 0;
This query will query out subplainid from table where will take last id and formularymixtype is not equal to 0
SELECT subplainid FROM table t
where id = (select max(id) from table where formularymixtype <> 0 )

Classic ASP / MSSQL - Remove returned results based on certain conditions

I have a little sql query, like so
SELECT * FROM table
This returns a bunch of results, i output the following fields:
ID
UserID
Amount
Date
What i want to do is get the most recent entry from each UserID ( based on ID ), then if the amount is 0 do not return ANY results from that UserID.
select t1.*
from your_table t1
join
(
select userid, max(date) as mdate
from your_table
group by userid
having sum(case when amount = 0 then 1 else 0 end) = 0
) t2 on t1.userid = t2.userid and t1.date = t2.mdate
In the subquery you group by the user and select only those having no amount of zero. In that select you use max(date) as mdate to get the latest date for each user.
That subquery can be joined to the original table to get the complete record and not just the userid.
try this
WITH cte AS
(
SELECT
MAX(ID) OVER (PARTITION BY UserID) MaxIDForUserID,
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY ID DESC) rn,
UserID,
Amount,
Date
FROM TableName
)
SELECT * FROM cte WHERE rn = 1 AND Amount != 0

Get entries which are mostly available

For example I have the following database entries:
timestamp | value1 | value 2
----------
1452|5|7
1452|1|6
1452|2|7
1623|1|2
1623|5|6
1623|4|5
1623|4|7
1855|1|2
Now I want to have a sql query which returns me value1 only for the timestamp which is availble the most. Therefore it should return only the timestamp 1623 and it's values.
I was first thinking of count, but that will return only the number of the availability and not the entries.
select *
from T
inner join (select timestamp
from T
group by timestamp
order by count(*) desc
limit 1) t2
on T.timestamp = t2.timestamp
see it's working live in a sqlfiddle
WITH CTE AS (
SELECT *, COUNT(timestamps) OVER (PARTITION BY value1, timestamps) AS cnt
FROM mytable
), cte2 as (select *, row_number() over (partition by value1 order by cnt DESC, timestamps) as Rn FROM cte)
SELECT value1, timestamps , cnt FROM CTE2 WHERE Rn = 1;