How to obtain a value from previous day in a query - mysql

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

Related

Query for finding latest record in sql

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');

SQL: Select the last record for each day given datetime

I have a table of stock data (the db is MySQL):
trade_time price
2013-01-02 09:43:00 515.61
2013-01-03 09:39:00 525.90
2013-01-03 11:38:00 539.44
2013-01-03 13:22:00 509.16
2013-01-04 09:47:00 507.40
2013-01-04 14:33:00 517.45
2013-01-07 09:33:00 550.42
2013-01-07 13:13:00 524.85
2013-01-07 14:51:00 536.44
I would like to return the last traded price for each day
trade_date price
2013-01-02 515.61
2013-01-03 509.16
2013-01-04 517.45
2013-01-07 536.44
What makes this question different from other "selecting the latest record based on datetime" questions on this site is that input is in datetime but output is in date. Let me know this question has already been answered.
You may join to a subquery which finds the maximum datetime for each date.
SELECT t1.trade_time, t1.price
FROM yourTable t1
INNER JOIN
(
SELECT DATE(trade_time) AS trade_date, MAX(trade_time) AS max_trade_time
FROM yourTable
GROUP BY DATE(trade_time)
) t2
ON t2.trade_date = DATE(t1.trade_time) AND
t2.max_trade_time = t1.trade_time
ORDER BY
t1.trade_time;
Demo
Here is an efficient solution using window function ROW_NUMBER() over a type cast of the timestamp column to date:
select trade_date, price
from (
select trade_date, price, row_number() over
(partition by date(trade_date)
order by trade_date desc) as row_num
from stock) as subquery
where row_num = 1
order by trade_date
You can use a correlated subquery:
select t.*
from t
where t.trade_date = (select max(t2.trade_date)
from t t2
where date(t2.trade_date) = date(t.trade_date)
);
I understand this is an implementation in MY-SQL, but I tried in SQL-Server just for fun
and this query gives the requisite output:
SELECT CAST(SQ.TT AS DATE) AS TRADED_DATE, PRICE
FROM STOCK
INNER JOIN
(SELECT MAX(TRADE_TIME) AS TT
FROM STOCK
GROUP BY CAST(TRADE_TIME AS DATE)) SQ
ON SQ.TT = TRADE_TIME
Output as
TRADED_DATE PRICE
----------- ---------------------------------------
2013-01-02 515.61
2013-01-03 509.16
2013-01-04 517.45
2013-01-07 536.44
(4 rows affected)

MySQL - how to find difference date between each row

Hi Programming Master,
I need help with this. This is an employee data, the NULL in TerminationDate refer to current date, it's mean that the employee are still working.
I need to find longest time (in days) where there is no one hired or terminated.
Table Name : Employee
Column Name : ID, HireDate, TerminationDate
Employee
ID HireDate TerminationDate
1 2009-06-20 2016-01-01
2 2010-02-12 NULL
3 2012-03-14 NULL
4 2013-09-10 2014-01-01
5 2013-09-10 NULL
6 2015-04-10 2015-05-01
7 2010-04-11 2016-01-01
8 2012-05-12 NULL
9 2011-04-13 2015-02-13
I have developed process of what need to do
Combine data in HireDate and TerminationDate (it should have 18 rows)
Order the date
Find the difference between each date from Row(n) and Row(n-1)
Get the max difference
However I don't know how to do it in MySQL or if it is even possible. I wonder if there is any other method? Please help me
This is rather complicated in MySQL, prior to version 8. But you can do:
select dte, next_dte,
datediff(coalesce(next_dte, curdate()), dte) as diff
from (select dte,
(select min(d2.dte)
from ((select hiredate as dte
from t
) union -- intentionally remove duplicates
(select terminationdate as dte
from t
where teminationdate is not null
)
) d2
where d2.dte > d.dte
) next_dte
from ((select hiredate as dte
from t
) union -- intentionally remove duplicates
(select terminationdate as dte
from t
where teminationdate is not null
)
) d
) d
order by diff desc
limit 1;
Note that this finds the the most recent period, based on the current date. You can adjust this by replacing curdate() with whatever cutoff date you have in mind. If you don't want the most recent period, add where next_dte is not null to the outer query.

How to sum up records from starting month to current per month

I've searched for this topic but all I got was questions about grouping results by month. I need to retrieve rows grouped by month with summed up cost from start date to this whole month
Here is an example table
Date | Val
----------- | -----
2017-01-20 | 10
----------- | -----
2017-02-15 | 5
----------- | -----
2017-02-24 | 15
----------- | -----
2017-03-14 | 20
I need to get following output (date format is not the case):
2017-01-20 | 10
2017-02-24 | 30
2017-03-14 | 50
When I run
SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)
I got sum per month of course.
Nothing comes to my mind how to put in nicely in one query to achieve my desired effect, probably W will need to do some nested queries but maybe You know some better solution.
Something like this should work (untestet). You could also solve this by using subqueries, but i guess that would be more costly. In case you want to sort the result by the total value the subquery variant might be faster.
SET #total:=0;
SELECT
(#total := #total + q.sum) AS total, q.date
FROM
(SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)) AS q
You can use DATE_FORMAT function to both, format your query and group by.
DATE_FORMAT(date,format)
Formats the date value according to the format string.
SELECT Date, #total := #total + val as total
FROM
(select #total := 0) x,
(select Sum(Val) as Val, DATE_FORMAT(Date, '%m-%Y') as Date
FROM st where Date >= '2017-01-01' and Date <= '2017-12-31'
GROUP BY DATE_FORMAT(Date, '%m-%Y')) y
;
+---------+-------+
| Date | total |
+---------+-------+
| 01-2017 | 10 |
+---------+-------+
| 02-2017 | 30 |
+---------+-------+
| 03-2017 | 50 |
+---------+-------+
Can check it here: http://rextester.com/FOQO81166
Try this.
I use yearmonth as an integer (the year of the date multiplied by 100 plus the month of the date) . If you want to re-format, your call, but integers are always a bit faster.
It's the complete scenario, including input data.
CREATE TABLE tab (
dt DATE
, qty INT
);
INSERT INTO tab(dt,qty) VALUES( '2017-01-20',10);
INSERT INTO tab(dt,qty) VALUES( '2017-02-15', 5);
INSERT INTO tab(dt,qty) VALUES( '2017-02-24',15);
INSERT INTO tab(dt,qty) VALUES( '2017-03-14',20);
SELECT
yearmonths.yearmonth
, SUM(by_month.month_qty) AS running_qty
FROM (
SELECT DISTINCT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
FROM tab
) yearmonths
INNER JOIN (
SELECT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
, SUM(qty) AS month_qty
FROM tab
GROUP BY YEAR(dt) * 100 + MONTH(dt)
) by_month
ON yearmonths.yearmonth >= by_month.yearmonth
GROUP BY yearmonths.yearmonth
ORDER BY 1;
;
yearmonth|running_qty
201,701| 10.0
201,702| 30.0
201,703| 50.0
select succeeded; 3 rows fetched
Need explanations?
My solution has the advantage over the others that it will be re-usable without change when you move it to a more modern database - and you can convert it to using analytic functions when you have time.
Marco the Sane

mysql query to find sum of fields grouping by time change

I have the following data structure
timestamp(varchar) bid(decimal) ask(decimal)
20090501 03:01:01.582 0.000060 0.000000
20090501 15:01:01.582 0.000120 0.000060
20090501 16:01:01.582 -0.000080 0.000120
20090504 03:01:01.582 0.000040 0.000060
20090504 15:01:01.582 -0.000040 0.000040
20090504 16:01:01.582 0.000000 -0.000040
20090505 03:01:01.582 0.000050 0.000110
20090505 15:01:01.582 0.000000 0.000050
20090505 16:01:01.582 -0.000080 0.000000
Now I want the output like the following
timestamp sum (bid) sum(ask)
20090501 15:01:01.582 0.000180 0.000060
20090504 15:01:01.582 -0.000080 0.000220
20090505 15:01:01.582 0.000050 0.000120
Now the relational logic behind the result is every time 15:01 occurs it will sum all the bids and asks value within the interval of last 15:01 occurrence it means the sum of bids and asks between every 15:01 need to be calculated
I am trying this with MySQL, so any help on this is highly appreciable.
The code which I have done so far is on Sql server 2008 R2
select date=case when substring(timestamp,10,2) <= 15
then substring(timestamp,1,8) else DATEADD("dd",1,substring(timestamp,1,8)) end,
SUM(isnull([Bid Change],0)), SUM([Ask Change]), MAX(aveg),MIN(aveg) from tbltestnew1
group by (case when substring(timestamp,10,2) <= 15
then substring(timestamp,1,8) else DATEADD("dd",1,substring(timestamp,1,8)) end),
CURR;
This gives me the result considering 1 day interval of every 15:01 which is not the correct result!
With MSSQL you could use an outer apply like this:
select
cast(t.timestamp as date) date,
bid_sum,
ask_sum
from tbltestnew1 t
outer apply (
select top 1 timestamp tlag
from tbltestnew1
where t.timestamp > timestamp and cast(timestamp as time) = '15:01:01.582' order by timestamp desc
) tprev
outer apply (
select sum(bid) bid_sum, sum(ask) ask_sum
from tbltestnew1
where (tlag is not null and (cast(timestamp as datetime) between dateadd(second,1, tlag) and t.timestamp)
or (tlag is null and cast(timestamp as datetime) <= t.timestamp)
)
) x
where cast(t.timestamp as time) = '15:01:01.582';
Sample SQL Fiddle
This query would give this result:
| DATE | BID_SUM | ASK_SUM |
|------------|----------|---------|
| 2009-05-01 | 0.00018 | 0.00006 |
| 2009-05-04 | -0.00008 | 0.00022 |
| 2009-05-05 | 0.00005 | 0.00012 |
With MSSQL 2012+ you could have used the lag() window function to access previous rows (which is what the first outer apply does), it would look like this:
select cast(t.timestamp as date) date, sum_bid, sum_ask
from (select timestamp, ask, bid, lag(timestamp) over (order by timestamp) prev from tbltestnew1
where cast(timestamp as time) = '15:01:01.582') t
outer apply (
select sum(bid) sum_bid, sum(ask) sum_ask
from tbltestnew1
where (prev is not null and (cast(timestamp as datetime) between dateadd(second,1, prev) and t.timestamp)
or (prev is null and cast(timestamp as datetime) <= t.timestamp))
) oa
Of course you could reduce the number of casts by using a common table expression (or derived tables).
select
case when time(timestamp) > '15:01:00'
THEN DATE(DATE_ADD(timestamp INTERVAL 1 DAY))
ELSE DATE(timestamp) END AS count_date,
SUM(isnull([Bid Change],0)),
SUM([Ask Change]),
MAX(aveg),
MIN(aveg) from tbltestnew1
group by count_date;
Based on your sample data, it seems to be as simple as GROUP BY LEFT(timestamp, 8).