Query data in time interval that spans two days in mysql - mysql

I need to count data for a given <week | month | custom> interval grouped by a given time schedule that possibly spans 2 days. The chunks depends on customers working schedules.
Possible cases (for one month interval) :
all data from June 01, 2022 to July 01, 2022, each day between 08:00 pm and 04:00 am (overnight)
all data from June 01, 2022 to June 30, 2022, each day between 04:00 am and 08:00 pm
all data from June 01, 2022 to June 30, 2022, each day between 00:00 am and 23:59 pm
Here's what I came up with:
WITH RECURSIVE seq AS (
SELECT
0 AS value
UNION ALL
SELECT
value + 1
FROM
seq
WHERE
value < 29
),
period AS (
SELECT
'2022-06-01 20:00' + INTERVAL (value * 24 * 60) MINUTE AS start,
'2022-06-01 20:00' + INTERVAL (value * 24 * 60) MINUTE + INTERVAL (8* 60) MINUTE AS end
FROM seq
ORDER BY value DESC
)
SELECT *
FROM (
SELECT
DATE(sd.timestamp - INTERVAL(LEAST(20, 4)) HOUR) as date,
SUM(...) as count,
FROM sensor_data sd
WHERE sd.timestamp BETWEEN '2022-06-01 20:00' AND '2022-07-01 04:00'
AND HOUR(sd.timestamp) >= 20 AND HOUR(sd.timestamp) < 4
GROUP BY
date
) main_data
INNER JOIN period ON DATE(period.start) = date
Unfortunately in doesn't work for the first case (spans two days). Any ideas?

WITH RECURSIVE
cte AS (
SELECT datetime_from range_from,
datetime_from + INTERVAL range_length HOUR range_till
UNION ALL
SELECT range_from + INTERVAL 1 DAY,
range_till + INTERVAL 1 DAY
FROM cte
WHERE range_till < datetime_till
)
SELECT range_from, range_till, COUNT(*) rows_amount
FROM cte
LEFT JOIN test ON test.dt BETWEEN range_from AND range_till
GROUP BY 1, 2;
DEMO with some explanations.

Related

How to get date increments in between start date and end date in MYSQL

I have sample dates in a table and what I need to get is each of the months between the start date and end date.
sample :
ID Startdate EndDate
1 01-01-2019 01-03-2019
2 01-08-2019 01-02-2020
I need to fetch months and year from these dates.
Desired output :
ID Dates
1 January 2019
1 February 2019
1 March 2019
2 August 2019
2 September 2019
2 October 2019
2 November 2019
2 December 2019
2 January 2020
2 February 2020
How cah I achieve this in MySQL and how to do increment or any loop kind of operation. On the query side I'm not getting any idea to move on this.
Here are a couple of ways to achieve this. The first will only work on MySQL 8+ and uses a recursive CTE to generate the months between StartDate and EndDate:
WITH RECURSIVE CTE AS (
SELECT ID, Startdate AS d, EndDate
FROM dates
UNION ALL
SELECT ID, d + INTERVAL 1 MONTH, EndDate
FROM CTE
WHERE d < EndDate
)
SELECT ID, DATE_FORMAT(d, '%M %Y') AS Dates
FROM CTE
ORDER BY ID, d
The second (which will run on any version of MySQL) uses a numbers table (in this case numbers from 0 to 99, allowing for a range of up to 99 months between StartDate and EndDate; if you need longer, adding more tables to the CROSS JOIN will increase that range by a factor of 10 for each table added) to generate the list of months difference, this is then JOINed to the original table so that the generated date Startdate + INTERVAL n.n MONTH is less than or equal to EndDate:
SELECT ID, DATE_FORMAT(Startdate + INTERVAL n.n MONTH, '%M %Y') AS Dates
FROM dates
JOIN (
SELECT n10.n * 10 + n1.n * 1 AS n
FROM (
SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n10
CROSS JOIN (
SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n1
) n ON Startdate + INTERVAL n.n MONTH <= EndDate
ORDER BY ID, Startdate + INTERVAL n.n MONTH
Having generated our list of dates, we format it using DATE_FORMAT and a format string of %M %Y. For both queries the output is:
ID Dates
1 January 2019
1 February 2019
1 March 2019
2 August 2019
2 September 2019
2 October 2019
2 November 2019
2 December 2019
2 January 2020
2 February 2020
Demo on dbfiddle

Mysql select rows based on previous quarterly month from current month

I want to query row to view registered record based on previous quarterly month, for example:
current month / year: Jan 2018
the query should show all records by registered month which is: Oct 2017, Jul 2017, Apr 2017 and so on.
I use below query but only can select rows for last 3 month.
SELECT name, date, amount, agreement, bank
FROM `account`
WHERE (YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 3 MONTH) AND MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 3 MONTH))
You can do arithmetic like this:
select a.*
from account a
where mod(month(date), 3) = mod(month(current_date), 3);
You seem to understand how to handle the year component, but something like:
and date >= curdate() - interval 1 year
Try This One, Manually
select *
from account
Where current_date >= dateadd(year, -1, Getdate())
And Month(current_date) In (
Month(Getdate()),
(Case When Month(Getdate())+3 > 12 Then Month(Getdate())+3-12 Else Month(Getdate())+3 End),
(Case When Month(Getdate())+6 > 12 Then Month(Getdate())+6-12 Else Month(Getdate())+6 End),
(Case When Month(Getdate())+9 > 12 Then Month(Getdate())+9-12 Else Month(Getdate())+9 End)
)

How to get last 2 or 3 months data in MySQL?

I m trying to get last 2 and 3 months data.
e.g Last 2 months data means Dec 2014 To Jan 2015 and 3 months means Nov 2014 TO Jan 2015
I tried
SELECT * FROM d_jobs WHERE `job_date` >= DATE_ADD( NOW( ) , INTERVAL -1 MONTH )
Here Its Count last 30 days data only .
So Which is the best way to perform this ?
Try this:
SELECT * FROM d_jobs WHERE `job_date` >= last_day(now()) + interval 1 day - interval 3 month;
Try with this query hope you will overcome your problem i think
select * from d_jobs where job_date < Now() and job_date > DATE_ADD(Now(), INTERVAL- 3 MONTH);
select * from d_jobs where job_date BETWEEN SUBDATE(CURDATE(), INTERVAL 3 MONTH) AND NOW();
select * from d_jobs WHERE job_date BETWEEN STR_TO_DATE('2012-09-01', '%Y-%m-%d') AND NOW();
Get left 7 chars of current date in ISO format (i.e. '2015-01'). Then add "-01" for 1st day of month (result is "2015-01-01"). Then use INTERVAL ... MONTH to get last months.

Finding percentage difference from different values in the same SQL Table?

I have a table that tracks total values for months against years in a particular location.
Desired Outcome: I wanted to compare a month's value for the current year against last years value. I then wanted to check for a percentage increase.
e.g. 2014 (January) = 140 - 2013 (January) = 150 * 100 = - 6.67
Table Name- donation_tracker
Thank you in advance.
As I understood, You want to get the percent of increase from last year to current year for the Same month for a Particular location. Use the query.
SELECT D1.month, ROUND((D2.Donation_amount- D1.Donation_amount) * 100 /
D1.Donation_amount, 2)
FROM donation_tracker D1
INNER JOIN donation_tracker D2
ON d1.month = D2.month AND D1.year = D2.year - 1
AND D1.Location_ID = D2.Location_ID;
Let's say you need to compare the immediately-completed twelve months with the twelve months prior to that, month-by-month. I am guessing at your table and column names because, well, I don't know them.
Let's build this from the ground up.
Here's a query that will find the most recent twelve months of donations month by month.
SELECT YEAR(donation_date) AS donation_year,
MONTH(donation_date) AS donation_month,
SUM(donation_amount) AS donation_amount
FROM donations
WHERE donation_date >= LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 13 MONTH
AND donation_date < LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
GROUP BY YEAR(donation_date), MONTH(donation_date)
That gives you a twelve-row result set like this (when NOW() happens to be in the middle of November 2014):
2013 11 145
2013 12 220
2014 1 123
2014 2 11
...
2014 10 45
The trick is picking the right range of donation_date values.
So, now you need two of those result sets, one for mostly-2014 and one for mostly-2013. The one for mostly-2013 looks very similar. You simply back up one more year like this.
SELECT YEAR(donation_date) AS donation_year,
MONTH(donation_date) AS donation_month,
SUM(donation_amount) AS donation_amount
FROM donations
WHERE donation_date >= LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 25 MONTH
AND donation_date < LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 13 MONTH
GROUP BY YEAR(donation_date), MONTH(donation_date)
This is going to be one of those notorious club-sandwich queries, made of those two basic queries. You join them by month like so, then do the percentage computation in the SELECT clause.
SELECT a.donation_month,
a.donation_amount AS this_year,
b.donation_amount AS last_year,
100.0 * (a.donation_amount - b.donation_amount) / b.donation_amount as pct_increase
FROM (
/* this year's query */
) AS a
JOIN (
/* last year's query */
) AS b ON a.donation_month = b.donation_month
ORDER BY a.donation_year, a.donation_month
Here's the whole club sandwich for your server to chew on. Yummy!
SELECT a.donation_month,
a.donation_amount AS this_year,
b.donation_amount AS last_year,
100.0 * (a.donation_amount - b.donation_amount) / b.donation_amount as pct_increase
FROM (
SELECT YEAR(donation_date) AS donation_year,
MONTH(donation_date) AS donation_month,
SUM(donation_amount) AS donation_amount
FROM donations
WHERE donation_date >= LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 13 MONTH
AND donation_date < LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
GROUP BY YEAR(donation_date), MONTH(donation_date)
) AS a
JOIN (
SELECT YEAR(donation_date) AS donation_year,
MONTH(donation_date) AS donation_month,
SUM(donation_amount) AS donation_amount
FROM donations
WHERE donation_date >= LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 25 MONTH
AND donation_date < LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 13 MONTH
GROUP BY YEAR(donation_date), MONTH(donation_date)
) AS b ON a.donation_month = b.donation_month
ORDER BY a.donation_year, a.donation_month
Once you stack up the whole club sandwich, it look complicated. But it's actually a stack of simple subqueries.
This should give you an idea :)
Sample data:
CREATE TABLE t
(`month` varchar(3), `year` int, `amount` int)
;
INSERT INTO t
(`month`, `year`, `amount`)
VALUES
('jan', 2013, 150),
('feb', 2013, 180),
('jan', 2014, 140),
('feb', 2014, 160)
;
Query:
select
t1.month, round((t2.amount - t1.amount) * 100 / t1.amount, 2)
from
t t1
inner join t t2 on t1.month = t2.month and t1.year < t2.year;
Result:
| MONTH | ROUND((T2.AMOUNT - T1.AMOUNT) * 100 / T1.AMOUNT, 2) |
|-------|-----------------------------------------------------|
| jan | -6.67 |
| feb | -11.11 |

Mysql date_sub interval 12 month

I'm trying to get all posts from the 12 last month, group by month. I have a quite correct query:
SELECT MONTH(time) as mois, YEAR(time) as annee, count(*) as nbre
FROM touist_stories
WHERE time >= DATE_SUB(now() + INTERVAL 1 MONTH, INTERVAL 2 YEAR)
group by MONTH(time)
order by YEAR(time) DESC, MONTH(time) DESC
But one month is always missing : november 2012
I tryied to add
+ INTERVAL 1 MONTH
to now() but it still missing... How can I get the 12 last month and not the 11 ones please?
Thanks
To get one year ago, here's a technique I've used in the past. Using #mysql variables, create a date based on the first day of a given month/year (via now()), then subtract 12 months. This example will get from Oct 1, 2012 to current -- which will include current Oct 2013. To exclude that, just add to where clause where I re-added 1 year so it goes from Oct 1, 2012 at 12:00:00 am to LESS THEN Oct 1, 2013 12:00:00.
SELECT
MONTH(time) as mois,
YEAR(time) as annee,
count(*) as nbre
FROM
touist_stories,
( select #lastYear := date_add( DATE_FORMAT(NOW(),
'%Y-%m-01'), interval -11 month) ) sqlvar
WHERE
time >= #lastYear
group by
MONTH(time)
order by
YEAR(time) DESC,
MONTH(time) DESC
Revised to make it go 11 months back (to November per example), and include UP TO AND INCLUDING all Current October activity.
For realy want on year data use 11 MONTH not 12
SELECT time
FROM touist_stories
WHERE time
BETWEEN
date_sub(Now(), INTERVAL 11 MONTH)
AND
Now();