Get running totals for each month based on created_at date - mysql

I'm trying to get a running total for each month of this year. So my ideal result would be something such as:
January | 4
February | 5
March | 8
April | 10
May | 12
June | 14
July | 16
August | 17
September | 18
October | 21
November | 22
December | 22
The basic count would just check against first of the month such as:
January 1 (sum where created_at < January 1, 2018)
February 1 (sum where created_at < February 1, 2018)
March 1 (sum where created_at < March 1, 2018)
April 1 (sum where created_at < April 1, 2018)
...
Doing it for one month at a time is easy, as I can just do something like this:
SELECT *
FROM businesses
WHERE created_at < CONCAT(YEAR(CURDATE()), "-12-01 00:00:01")
I tried using one of the examples from another Stackoverflow answer, but it's not working quite as desired as it seems like the sort or something is messed up so the counts are not lining up right.
You can see schema build and current SQL here:
http://sqlfiddle.com/#!9/0c23cc/20

Try something like this.
SELECT
MONTHNAME( created_at ) AS theMonth,
(
SELECT
count( * )
FROM
businesses AS u1
WHERE
YEAR ( u1.created_at ) = YEAR ( businesses.created_at )
AND u1.created_at <= date_ADD( last_day( businesses.created_at ), INTERVAL 1 DAY )
) AS totals
FROM
businesses
WHERE
YEAR ( businesses.created_at ) = YEAR ( CURDATE( ) )
GROUP BY
MONTHNAME( businesses.created_at )
ORDER BY
MONTH ( businesses.created_at ) ASC

Related

Query data in time interval that spans two days in 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.

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

sql query range selection

Hi i have a table like below
year | month | otherdata
2015 1
2015 1
2015 3
2015 4
2015 4
2016 1
2016 2
2016 2
here how do i select all data of (year 2015/month 4 to year 2016/month 2)
i tried following query.
select * from `schedule_details` where (`year` >= 2015 and `month` >= 4) and (`year` <= 2016 and `month` >= 2)
but its not working. how do i do this? please help
thanks
Solved the problem
Select *
From schedule_details
Where ( year = 2015 And month >= 4 )
Or ( year > 2015 And year < 2016 )
Or ( year = 2016 And month <= 2 )
is there any other better way?

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 select unique months when discounts are valid

I have a MySQL database containing discounts. A simplified version looks like this:
id | start (UNIX timestamp) | end (UNIX timestamp)
45 | 1384693200 | 1398992400
68 | 1386018000 | 1386277200
263 | 1388530800 | 1391209200
A discount can last a few days, a few months, or even a few years. I'm looking for a way to select a unique list of months where (future) discounts are valid.
If there is:
a discount which starts in november 2013 and ends in april 2014
a discount which starts in december 2013 and ends in the same month
a discount which starts in january 2014 and ends one month later
a discount which starts in june 2014 and ends the same month
The output should be:
- December (2013)
- January (2014)
- February (2014)
- March (2014)
- April (2014)
- June (2014)
November 2013 is not shown because it is in the past. May 2014 is not shown because there is no discount in that month.
Can somebody help?
Thanks in advance!
Create a table containing a sequence of numbers from 0 to a number of month you could ever require, and join this table to your table.
This is example how to get a list of years+months separately for each id
SELECT id,
year( start + interval x month ) year,
month( start + interval x month ) month
FROM
numbers n
JOIN
(
SELECT id,
from_unixtime( start ) start,
from_unixtime( end ) end
FROM Table1
) q
ON n.x <= period_diff( date_format( q.end, '%Y%m' ),date_format( q.start, '%Y%m' ))
ORDER BY id, year, month ;
Demo --> http://www.sqlfiddle.com/#!9/d7cfc/4
If you want to combine years+months for all id, skip id column and use GROUP BY
SELECT year( start + interval x month ) year,
month( start + interval x month ) month
FROM
numbers n
JOIN
(
SELECT id,
from_unixtime( start ) start,
from_unixtime( end ) end
FROM Table1
) q
ON n.x <= period_diff( date_format( q.end, '%Y%m' ),date_format( q.start, '%Y%m' ))
GROUP BY year, month
ORDER BY year, month ;
If you want to skip past years and months, add WHERE year >= current year AND month >= current month, this is a trivial change. Also add another WHERE end < current-unix-time in the subquery to filter out unwanted past rows.