SQL - Filtering by closest date [duplicate] - sql

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 4 years ago.
Let's say I have a table:
SELECT * FROM table;
Ric Charge Date
VOD.L 2 20180601
VOD.L 5 20181002
VOD.L 4.5 20180212
RBS.L 3 20180504
RBS.L 6 20180708
How could I filter, by date, such that it will return ONLY the most recent charge
E.g.
Ric Charge Date
VOD.L 4.5 20180212
RBS.L 6 20180708

I would prefer simple top 1
select top 1 * from t order by date desc
As you edit question, you can use co-related subquery byt your sample output is wrong
select * from t
where t.date in (select max(t1.date) from t t1
where t1.Ric=t.Ric
)
Demo
Ric Charge Date
VOD.L 5.000 02/10/2018 00:00:00
RBS.L 6.000 08/07/2018 00:00:

Something like
SELECT * FROM table WHERE date = (SELECT MAX(date) FROM table)
does what you ask(in multiple DB too)..
Date is a keyword in SQL and makes for a poor column name. If you can, change it now. If you can't, you might need to quote it according to the database you have

Order by the date and then use LIMIT 1 to only get one record.
SELECT *
FROM table
ORDER BY date DESC
LIMIT 1;

I would use row_number() with ties clause :
select top (1) with ties t.*
from table t
order by row_number() over (partition by ric order by date desc);
If the date has ties then you can use dense_rank() instead of row_number().

A self left join with an inequality would work:
SELECT t1.*
FROM
table t1
LEFT JOIN table t2 ON
t1.date < t2.date
WHERE
t2.date IS NULL
GROUP BY
t1.Ric
;

You can use a window function such as ROW_NUMBER to create a windowed order number, then you can extract only the record with row number = 1 (the latest date):
declare #tmp table (Ric varchar(50), Charge numeric(10,3), [Date] Date)
insert into #tmp values
('VOD.L', 2 , '20180601')
,('VOD.L', 5 , '20181002')
,('VOD.L', 4.5, '20180212')
,('RBS.L', 3 , '20180504')
,('RBS.L', 6 , '20180708')
select t.Ric, t.Charge, t.[Date]
from (
select ric,
Charge,
row_number() over (partition by ric order by [Date] desc) as rn,
[Date]
from #tmp
) t where rn = 1
Result:
From your question it is not clear what sholud happen when there are more rows with the same Ric/date values

As an alternative, you could use not exists.
SELECT *
FROM TableA AS A
WHERE NOT EXISTS ( SELECT * FROM TableA AS A2
WHERE A.Ric = A2.Ric
AND A2.Date > A.Date )

Related

Select multiple max values after GROUP BY query

Suppose I have a table look like this:
date ID income
0 9/1 C 10.40
1 9/3 A 33.90
2 9/3 B 29.10
3 9/4 C 19.30
4 9/4 B 17.80
5 9/5 B 9.55
6 9/5 C 11.10
7 9/5 A 13.10
8 9/7 A 29.10
9 9/7 B 29.10
I want to find out the ID who made the most income for each date. The most intuitive approach would be writing
SELECT ID, MAX(income) FROM table GROUP BY date
But there are two IDs who made the same MAX income on 9/7, I want to retain all ties on the same date, by using that query I will ignore one ID on 9/7, and 29.1 appears on 9/3 and 9/7, any other approach?
A join based approach doesn't have this problem, and would retain all records tied for the max income on a given date.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT date, MAX(income) AS max_income
FROM yourTable
GROUP BY date
) t2
ON t1.date = t2.date AND t1.income = t2.max_income
ORDER BY
t1.date;
The way the above query works is to join the complete original table to a subquery which finds, for each date, the maximum income value. This has the effect of filtering off any record which did not have the max income on a given date. Pay close attention to the join condition, which has two components, the date, and the income.
If your database supports analytic function, we can also use RANK here:
SELECT date, ID, income
FROM
(
SELECT t.*, RANK() OVER (PARTITION BY date ORDER BY income DESC) rnk
FROM yourTable t
) t
WHERE rnk = 1
ORDER BY date;
one approach can be like below
with cte1
(
Select t1.*
FROM yourTable t1
INNER JOIN
(
SELECT date, MAX(income) AS max_income
FROM yourTable
GROUP BY date
) t2
ON t1.date = t2.date AND t1.income = t2.max_income
) select min(ID) as ID, date,income from cte1
group by date,income
As you not mentioned which id you need in case of two ID's(when income is same on a particular date) so i took minimum id among them when two id's income is same on a particular date But at the same time you may use max() function also
Try below using subquery and as you've tie for one date so take minimum ID which'll give you one id from date 9/7
select date,min(ID),income
from
(SELECT t1.date, t1.ID,t1.income
FROM tablename t1
INNER JOIN
(
SELECT date, MAX(income) AS mincome
FROM yourTable
GROUP BY date
) t2 ON t1.date = t2.date AND t1.income = t2.mincome
)X group by date,income

How to select top 1 and ordered by date in Oracle SQL? [duplicate]

This question already has answers here:
How do I do top 1 in Oracle? [duplicate]
(9 answers)
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Oracle SELECT TOP 10 records [duplicate]
(6 answers)
How to use Oracle ORDER BY and ROWNUM correctly?
(5 answers)
Closed 5 years ago.
There is a clear answer how to select top 1:
select * from table_name where rownum = 1
and how to order by date in descending order:
select * from table_name order by trans_date desc
but they does not work togeather (rownum is not generated according to trans_date):
... where rownum = 1 order by trans_date desc
The question is how to select top 1 also ordered by date?
... where rownum = 1 order by trans_date desc
This selects one record arbitrarily chosen (where rownum = 1) and then sorts this one record (order by trans_date desc).
As shown by Ivan you can use a subquery where you order the records and then keep the first record with where rownum = 1in the outer query. This, however, is extremely Oracle-specific and violates the SQL standard where a subquery result is considered unordered (i.e. the order by clause can be ignored by the DBMS).
So better go with the standard solution. As of Oracle 12c:
select *
from table_name
order by trans_date desc
fetch first 1 row only;
In older versions:
select *
from
(
select t.*, row_number() over (order by trans_date desc) as rn
from table_name t
)
where rn = 1;
Modern Oracle versions have FETCH FIRST:
select * from table_name order by trans_date desc
fetch first 1 row only
There should be subquery so the combination rownum & order could work:
select * from (select * from table_name order by trans_date desc) AS tb where rownum = 1
You can use window functions for that:
select t.*
from (
select *,
min(trans_date) over () as min_date,
max(trans_date) over () as max_date
from the_table
) t
where trans_date = min_date
or trans_date = max_date;
Another option would be to join on the derived table
select t1.*
from the_table
join (
select min(trans_date) over () as min_date,
max(trans_date) over () as max_date
from the_table
) t2 on t1.trans_date = t2.min_date
or t1.trans_date = t2.max_date;
Not sure which one would be faster, you need to check the execution plan

How to select id with max date group by category in PostgreSQL?

For an example, I would like to select id with max date group by category,
the result is: 7, 2, 6
id category date
1 a 2013-01-01
2 b 2013-01-03
3 c 2013-01-02
4 a 2013-01-02
5 b 2013-01-02
6 c 2013-01-03
7 a 2013-01-03
8 b 2013-01-01
9 c 2013-01-01
May I know how to do this in PostgreSQL?
This is a perfect use-case for DISTINCT ON - a Postgres specific extension of the standard DISTINCT:
SELECT DISTINCT ON (category)
id -- , category, date -- any other column (expression) from the same row
FROM tbl
ORDER BY category, date DESC;
Careful with descending sort order. If the column can be NULL, you may want to add NULLS LAST:
Sort by column ASC, but NULL values first?
DISTINCT ON is simple and fast. Detailed explanation in this related answer:
Select first row in each GROUP BY group?
For big tables with many rows per category consider an alternative approach:
Optimize GROUP BY query to retrieve latest row per user
Optimize groupwise maximum query
Try this one:
SELECT t1.* FROM Table1 t1
JOIN
(
SELECT category, MAX(date) AS MAXDATE
FROM Table1
GROUP BY category
) t2
ON T1.category = t2.category
AND t1.date = t2.MAXDATE
See this SQLFiddle
Another approach is to use the first_value window function: http://sqlfiddle.com/#!12/7a145/14
SELECT DISTINCT
first_value("id") OVER (PARTITION BY "category" ORDER BY "date" DESC)
FROM Table1
ORDER BY 1;
... though I suspect hims056's suggestion will typically perform better where appropriate indexes are present.
A third solution is:
SELECT
id
FROM (
SELECT
id,
row_number() OVER (PARTITION BY "category" ORDER BY "date" DESC) AS rownum
FROM Table1
) x
WHERE rownum = 1;

group rows in plain sql

I have a Table with columns Date and Number, like so:
date Number
1-1-2012 1
1-2-2012 1
1-3-2012 2
1-4-2012 1
I want to make a sql query that groups the rows with the same Number and take the minimum date. The grouping only may occur when the value iof Number is the same as previous / next row. So the rsult is
date Number
1-1-2012 1
1-3-2012 2
1-4-2012 1
try this:
WITH CTE AS(
SELECT * ,ROW_NUMBER() OVER (ORDER BY [DATE] ) -
ROW_NUMBER() OVER (PARTITION BY NUMBER ORDER BY [DATE] ) AS ROW_NUM
FROM TABLE1)
SELECT NUMBER,MIN(DATE) AS DATE
FROM CTE
GROUP BY ROW_NUM,NUMBER
ORDER BY DATE
SQL fiddle demo
SELECT Number, MIN(date)
FROM table
GROUP BY Number
ORDER BY Number
since you requirement is a bit more specific, how about this? I have not checked it myself, but something that might work, considering you requirement..
SELECT date, Number FROM (
SELECT Number,
(SELECT MIN(date) FROM #table t2 WHERE t1.date <> t2.date AND t1.Number = t2.Number) AS date
FROM table t1
) AS a
GROUP BY number, date

Select newest records that have distinct Name column

I did search around and I found this
SQL selecting rows by most recent date with two unique columns
Which is so close to what I want but I can't seem to make it work.
I get an error Column 'ID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I want the newest row by date for each Distinct Name
Select ID,Name,Price,Date
From table
Group By Name
Order By Date ASC
Here is an example of what I want
Table
ID
Name
Price
Date
0
A
10
2012-05-03
1
B
9
2012-05-02
2
A
8
2012-05-04
3
C
10
2012-05-03
4
B
8
2012-05-01
desired result
ID
Name
Price
Date
2
A
8
2012-05-04
3
C
10
2012-05-03
1
B
9
2012-05-02
I am using Microsoft SQL Server 2008
Select ID,Name, Price,Date
From temp t1
where date = (select max(date) from temp where t1.name =temp.name)
order by date desc
Here is a SQL Fiddle with a demo of the above
Or as Conrad points out you can use an INNER JOIN (another SQL Fiddle with a demo) :
SELECT t1.ID, t1.Name, t1.Price, t1.Date
FROM temp t1
INNER JOIN
(
SELECT Max(date) date, name
FROM temp
GROUP BY name
) AS t2
ON t1.name = t2.name
AND t1.date = t2.date
ORDER BY date DESC
There a couple ways to do this. This one uses ROW_NUMBER. Just partition by Name and then order by what you want to put the values you want in the first position.
WITH cte
AS (SELECT Row_number() OVER (partition BY NAME ORDER BY date DESC) RN,
id,
name,
price,
date
FROM table1)
SELECT id,
name,
price,
date
FROM cte
WHERE rn = 1
DEMO
Note you should probably add ID (partition BY NAME ORDER BY date DESC, ID DESC) in your actual query as a tie-breaker for date
select * from (
Select
ID, Name, Price, Date,
Rank() over (partition by Name order by Date) RankOrder
From table
) T
where RankOrder = 1
I have found another memory efficient way (but probably crude way)that has worked for me in postgress. Order the query by the date desc, then select the first record of each distinct field.
SELECT distinct on (Name) ID, Price, Date from
table
order by Date desc
Use Distinct instead of Group By
Select Distinct ID,Name,Price,Date
From table
Order By Date ASC
http://technet.microsoft.com/en-us/library/ms187831.aspx