Query for finding latest record in sql - mysql

Given a currency table I need to find the latest record of conversion rate which is less than given particular date
Input table structure given below:
id
baseCur
Curr
rate
date
1
INR
USD
81
2022-11-09
2
INR
USD
82
2022-11-08
3
INR
USD
80
2022-11-06
4
INR
CAD
56
2022-11-05
5
INR
RUB
.74
2022-11-04
6
INR
CAD
57
2022-11-12
Problem statement:
I need to find all latest currencies rate that is less than 2022-11-09.On any given date there will be only conversation rate for any particular currency
so expected output
id
baseCur
Curr
rate
date
2
INR
USD
82
2022-11-08
4
INR
CAD
56
2022-11-05
5
INR
RUB
.74
2022-11-04
Explanantion of output :
Id 1,6 rejected : cause they are greater than 2022-11-09 date
Id 3 rejected cause we have one more record for INR to CAD in row 2 and its date is more new to Id 3

You can use a window function such as DENSE_RANK() if DB version is 8.0+ in order to determine the latest records by using the query below
WITH t AS
(
SELECT t.*, DENSE_RANK() OVER (PARTITION BY baseCur, Curr ORDER BY date DESC) AS dr
FROM t
WHERE date < '2022-11-09'
)
SELECT id, baseCur, Curr, rate, date
FROM t
WHERE dr = 1
But, notice that this query returns the ties(equal date values) as well if there is any.
Demo

Beside the option to use a window function for that, you could also use a subquery. In the subquery, you will catch every currency with the latest date:
SELECT
curr, MAX(yourdate) maxDate
FROM yourtable
WHERE yourdate < '2022-11-09'
GROUP BY curr;
This query will produce this outcome:
Curr
maxDate
2
2022-11-08
4
2022-11-05
5
2022-11-04
This result can be used by applying a JOIN clause or IN clause from a main query.
This will add the other columns.
SELECT y.id, y.baseCur, y.curr, y.rate, y.yourdate
FROM yourtable y
JOIN (SELECT
curr, MAX(yourdate) maxDate
FROM yourtable
WHERE yourdate < '2022-11-09'
GROUP BY curr) maxDates
ON y.curr = maxDates.curr
AND y.yourdate = maxDates.maxDate
ORDER BY id;
Thus, the complete intended result will be created:
id
baseCur
Curr
rate
date
2
INR
USD
82
2022-11-08
4
INR
CAD
56
2022-11-05
5
INR
RUB
.74
2022-11-04
To point that out: I think using a window function should be prefered if possible.
They just have the "disadvantage" older DB's don't provide them and they often differ depending on the DB type.
So, if a query is required that works always on each DB type and DB version, this way of using a subquery becomes helpful.

You can fetch the desired output using subquery, as shown below, which fetches latest record from each currency.
-- 1. Based on id column
SELECT * FROM sometable as t WHERE t.id =
(SELECT MAX(id) FROM sometable WHERE Curr = t.Curr and date < '2022-11-09');
-- 2. Based on date column
SELECT * FROM sometable as t WHERE t.date =
(SELECT MAX(date) FROM sometable WHERE Curr = t.Curr and date < '2022-11-09');

Related

SQL calculation based on different conditions

I have one table and trying to subtract the total where a condition is True from the full total.
Ticket
Amount
Code
11
5.00
12
3.00
X
13
10.00
14
2.00
X
My query was
SELECT SUM(AMOUNT)
FROM Table
MINUS
SELECT SUM(Amount)
FROM Table
WHERE Code = 'X'
So the answer should be 20 - 5= 15
Below two possible queries:
-- Use IF operator
SELECT SUM(amount) - SUM(IF(code = 'X', amount, 0)) FROM tbl;
-- Use implicit MySQL conversion boolean to int (true => 1)
SELECT SUM(amount) - SUM(amount * (code = 'X')) FROM tbl;
SQL editor online

Get original RANK() value based on row create date

Using MariaDB and trying to see if I can get pull original rankings for each row of a table based on the create date.
For example, imagine a scores table that has different scores for different users and categories (lower score is better in this case)
id
leaderboardId
userId
score
submittedAt ↓
rankAtSubmit
9
15
555
50.5
2022-01-20 01:00:00
2
8
15
999
58.0
2022-01-19 01:00:00
3
7
15
999
59.1
2022-01-15 01:00:00
3
6
15
123
49.0
2022-01-12 01:00:00
1
5
15
222
51.0
2022-01-10 01:00:00
1
4
14
222
87.0
2022-01-09 01:00:00
1
5
15
555
51.0
2022-01-04 01:00:00
1
The "rankAtSubmit" column is what I'm trying to generate here if possible.
I want to take the best/smallest score of each user+leaderboard and determine what the rank of that score was when it was submitted.
My attempt at this failed because in MySQL you cannot reference outer level columns more than 1 level deep in a subquery resulting in an error trying to reference t.submittedAt in the following query:
SELECT *, (
SELECT ranking FROM (
SELECT id, RANK() OVER (PARTITION BY leaderboardId ORDER BY score ASC) ranking
FROM scores x
WHERE x.submittedAt <= t.submittedAt
GROUP BY userId, leaderboardId
) ranks
WHERE ranks.id = t.id
) rankAtSubmit
FROM scores t
Instead of using RANK(), I was able to accomplish this by with a single subquery that counts the number of users that have a score that is lower than and submitted before the given score.
SELECT id, userId, score, leaderboardId, submittedAt,
(
SELECT COUNT(DISTINCT userId) + 1
FROM scores t2
WHERE t2.userId = t.userId AND
t2.leaderboardId = t.leaderboardId AND
t2.score < t.score AND
t2.submittedAt <= t.submittedAt
) AS rankAtSubmit
FROM scores t
What I understand from your question is you want to know the minimum and maximum rank of each user.
Here is the code
SELECT userId, leaderboardId, score, min(rankAtSubmit),max(rankAtSubmit)
FROM scores
group BY userId,
leaderboardId,
scorescode here

mysql group by day and count then filter only the highest value for each day

I'm stuck on this query. I need to do a group by date, card_id and only show the highest hits. I have this data:
date card_name card_id hits
29/02/2016 Paul Stanley 1345 12
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Dave Mustaine 1349 10
25/02/2016 Ozzy 1351 17
23/02/2016 Jhonny Cash 1353 13
23/02/2016 Elvis 1355 15
20/02/2016 James Hethfield 1357 9
20/02/2016 Max Cavalera 1359 12
My query at the moment
SELECT DATE(card.create_date) `day`, `name`,card_model_id, count(1) hits
FROM card
Join card_model ON card.card_model_id = card_model.id
WHERE DATE(card.create_date) >= DATE(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND card_model.preview = 0
GROUP BY `day`, card_model_id
;
I want to group by date, card_id and filter the higher hits result showing only one row per date. As if I run a max(hits) with group by but I won't work
Like:
date card_name card_id hits
29/02/2016 Phil Anselmo 1347 16
25/02/2016 Ozzy 1351 17
23/02/2016 Elvis 1355 15
20/02/2016 Max Cavalera 1359 12
Any light on that will be appreciated. Thanks for reading.
Here is one way to do this. Based on your sample data (not the query):
select s.*
from sample s
where s.hits = (select max(s2.hits)
from sample s2
where date(s2.date) = date(s.date)
);
Your attempted query seems to have no relationship to the sample data, so it is unclear how to incorporate those tables (the attempted query has different columns and two tables).

How to obtain a value from previous day in a query

Given the data table below, how can you show the score for these date range: 3/10 to 3/12?
The formula for score is today's score=(today's avg5daysprice *2.15)/ yesterday's score. For example for 3/10 score = (126.11*2.15)/10.36
The data lives in both Sql server and mysql.
symbol tdate price avg5daysprice score
-----------------------------------------------
aapl 3/9/2015 127.14 126.6, 10.36
aapl 3/10/2015 125.32 126.11 null
aapl 3/11/2015 128.15 127.25 null
aapl 3/12/2015 124.48 125.66 null
In SQL Server 2012 you can use the LAG function to access data from the previous row. Try this:
SELECT symbol
,tdate
,price
,avg5daysprice
,COALESCE((avg5daysprice * 2.15) / LAG(score, 1, NULL) OVER (ORDER BY tdate), score) AS score
FROM YourTable
For a cross-RDBMS solution I would go with a self-join.
CTE is the solution in Sql Server, below the code
WITH Value_CTE (symbol, tdate,avg5day, scoreAnt) AS (
SELECT symbol, tdate,avg5day, score
FROM ScoreTable
WHERE score IS NOT NULL
YNION ALL
SELECT B.symbol, B.tdate, B.avg5day, cast(((B.avg5day*2.15)/A.scoreAnt) as decimal(8,2)) as score
FROM value_CTE A
INNER JOIN ScoreTable B ON DATEADD(DAY,-1,B.tdate) = A.tdate
)
-- Define the outer query referencing the CTE name.
SELECT symbol, tdate, avg5day, scoreAnt
FROM value_CTE
Result
symbol tdate avg5day scoreAnt
aapl 2015-03-09 126.60 10.36
aapl 2015-03-10 126.11 26.17
aapl 2015-03-11 127.25 10.45
aapl 2015-03-12 125.66 25.85

MySQL to calculate a value against price at the nearest date in another table

I have 2 tables where I want to calculate a value across. One table provides daily price data (prices) which is historic while the second contains annual data (volumes) which contains both historic and projected data. So far, I can join the two tables so that I can calculate the historic annual value by the price at the corresponding date in the daily one, but not for the future annual values where I would like to calculate against the last daily price in the prices table.
Current table structure
Table 1: Prices (3 fields: date,code,price)
Table 2: Volumes (3 fields: date,code,units)
Current query
SELECT v.date,p.price,v.units,CONCAT(p.price*v.units) AS Value
FROM Prices p,Volumes v
WHERE p.code = v.code AND p.date = v.date
AND v.code = 'X' AND (v.date BETWEEN '2012-12-31' AND '2016-12-31')
GROUP BY v.date
ORDER BY v.date
LIMIT 5;
results in 2012-2013 data rather than 2012-2016:
date 2012-12-31 2013-12-31
price 50 58
units 100 90
Value 5000 5220
My annual table (Volumes) has volume estimates for years 2014,2015 and 2016 (which are not shown) and the last price in the daily table (Prices) is say 65, but the above query will only match the volumes to the corresponding date value in the daily price table rather than using the nearest last value. Can someone advise me on how best to do this please?
example of tables (note: both tables use a composite primary key around 'date' and 'code')
Volumes
date code units
2012-12-31 X 100
2012-12-31 Y 50
2013-12-31 X 90
2013-12-31 Y 45
2014-12-31 X 95
2014-12-31 Y 47
Prices
date code price
2013-12-31 X 50
2013-12-31 Y 25
2014-01-01 X 58
2014-01-01 Y 27
2014-01-02 X 59
2014-01-02 Y 30
-----
2014-03-31 X 48
2014-03-31 Y 26
2014-04-01 X 49
2014-04-01 Y 27
last data point
If you want a query to get the future values by multiplying the future projected unit quantity with the most recent price recorded then this would be the query. see working FIDDLE
SELECT
*,
(future_units*last_price) as projeted_value --- dont need the CONCAT for this calculation
FROM(
SELECT
v.date,
v.units as future_units,
(SELECT
price
FROM prices
ORDER BY date desc --- desc for the most recent
LIMIT 1) as last_price --- just need the first one
FROM volumes v
WHERE v.date > NOW() --- to make it a future date
)as t --- table needs alias
updated query per the OP request.. working FIDDLE
This one calculates the price related with the date.. once you get to future dates it calculates the last price with the future units
SELECT
*,
if(DATE(the_date) > NOW(), --- if its a future date put in last_price, else put in the current_price
(future_units*last_price),
(future_units*current_price))AS value
FROM(
SELECT
v.date AS the_date,
v.units AS future_units,
v.code,
p.price AS current_price,
(SELECT
price
FROM prices
ORDER BY date DESC
LIMIT 1) AS last_price
FROM prices p
LEFT JOIN volumes v ON p.code = v.code
WHERE p.date >= v.date OR p.date < v.date
AND DATE(v.date) BETWEEN '2012-12-31' AND '2016-12-31' -- DATE() used to cut out timestamp for accurate comparison
GROUP BY v.date, v.code, v.units
)AS t