MySQL record between fixed date range - mysql

I have an small application which was build with CodeIgniter 3 and need to perform a report which will be converted to Chart.js. The report should be in yearly basis but at given specific date every month. The requirement are all data count must be from 4th to 3rd monthly. Like this:
For example January Report would be from 4th January to 3rd February, 4th February to 3rd March,... and so on.
I have created a MySQL query but I'm stuck on how to get the date too date. My Query are as follows:
SELECT DATE_FORMAT(odd_date_created, '%Y') as 'year',
DATE_FORMAT(odd_date_created, '%m') as 'month',
COUNT(odd_id) as 'total', status
FROM odd_data
WHERE status = $id and
GROUP BY DATE_FORMAT(odd_date_created, '%Y%m'), status
I'm new to MySQl. Could somebody help me on this. I'm stuck where should I put the date to date query.

Firstly I want to caution you not to use "between" with the following when you come to join your data, use this method instead data.date >= r.period_start_dt and data.date < r.period_end_dt
Secondly I am assuming your data does have dates or timestamps and that will fall between the calculated ranges that follow:
set #year :=2017;
select
*
from (
select
start_dt + INTERVAL m.n MONTH period_start_dt
, start_dt + INTERVAL m.n + 1 MONTH period_end_dt
from (
select str_to_date(concat(#year,'-01-04'),'%Y-%m-%d') start_dt ) seed
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 union all
select 10 union all
select 11
) m
) r
## LEFT JOIN YOUR DATA
## ON data.date >= r.period_start_dt and data.date < r.period_end_dt
Example ranges: (produce you own at this demo: http://rextester.com/CHTKSA95303 )
nb dd.mm.yyyy (.de format)
+----+---------------------+---------------------+
| | period_start_dt | period_end_dt |
+----+---------------------+---------------------+
| 1 | 04.01.2017 00:00:00 | 04.02.2017 00:00:00 |
| 2 | 04.02.2017 00:00:00 | 04.03.2017 00:00:00 |
| 3 | 04.03.2017 00:00:00 | 04.04.2017 00:00:00 |
| 4 | 04.04.2017 00:00:00 | 04.05.2017 00:00:00 |
| 5 | 04.05.2017 00:00:00 | 04.06.2017 00:00:00 |
| 6 | 04.06.2017 00:00:00 | 04.07.2017 00:00:00 |
| 7 | 04.07.2017 00:00:00 | 04.08.2017 00:00:00 |
| 8 | 04.08.2017 00:00:00 | 04.09.2017 00:00:00 |
| 9 | 04.09.2017 00:00:00 | 04.10.2017 00:00:00 |
| 10 | 04.10.2017 00:00:00 | 04.11.2017 00:00:00 |
| 11 | 04.11.2017 00:00:00 | 04.12.2017 00:00:00 |
| 12 | 04.12.2017 00:00:00 | 04.01.2018 00:00:00 |
+----+---------------------+---------------------+

Given the specification, I think I would tempted to cheat it... subtract 3 days from the date. Doing that, Jan 4 backs up to Jan 1, Feb 3 backs up to Jan 31... so those all end up as January.
SELECT DATE_FORMAT(odd_date_created + INTERVAL -3 DAY, '%Y') AS `year`
, DATE_FORMAT(odd_date_created + INTERVAL -3 DAY, '%m') AS `month`
, ...
FROM ...
GROUP
BY DATE_FORMAT(odd_date_created + INTERVAL -3 DAY, '%Y')
, DATE_FORMAT(odd_date_created + INTERVAL -3 DAY, '%m')
This falls apart if there's oddball ranges... if it's not always the 4th and 3rd.

Related

Records added since specific month this year in SQL

I have a table which contains the following data
userId | name | from
1 | aaa | 2020-09-23
2 | bbb | 2020-09-01
3 | ccc | 2019-05-12
4 | ddd | 2019-06-01
5 | eee | 2018-06-23
6 | fff | 2018-07-23
It is for an educational purpose and therefore the year runs from September to August rather than January to December. How do I output all of the users who were added since the previous September (so if it's October 2020 then 1 month ago, if it's January 2021 then 4 months ago)? The query has to be relative so that it always outputs the previous September to the time that the query is run rather than a specific September.
The result of the query should be
bbb 2020-09-01
aaa 2020-09-23
With NOT EXISTS:
select t.* from tablename t
where t.`from` >= concat(year(current_date), '-09-01')
and not exists (
select 1 from tablename
where name = t.name
and `from` between
concat(year(current_date), '-09-01') - interval 1 year
and
concat(year(current_date), '-09-01') - interval 1 day
)
Or maybe:
select t.* from tablename t
where t.`from` >= concat(year(current_date) - (month(current_date) < 9), '-09-01')
and not exists (
select 1 from tablename
where name = t.name
and `from` between
concat(year(current_date) - (month(current_date) < 9), '-09-01') - interval 1 year
and
concat(year(current_date) - (month(current_date) < 9), '-09-01') - interval 1 day
)
so if you execute the query in say March 2021, you will get the correct results that compare the current educational year with the previous one.
See the demo.
Results:
> userId | name | from
> -----: | :--- | :---------
> 1 | aaa | 2020-09-23
> 2 | bbb | 2020-09-01
You can subtract 9 months and compare to the year:
where year(`from` - interval 9 month) = year(curdate() - interval 9 month)
Actually, you might want year(curdate()) +/- 1 depending on how you are identifying the year.

How to select count today and tomorrow data less than specific time group by day?

I have a table like a table below.
I want to select count and group by day.
But the data in 1 day will start counts at 7:00:00 until tomorrow at 6:59:59 (24hr.).
For example
Day 1 data between '2019/06/01 7:00:00' and '2019/06/02 06:59:59'
Day 2 data between '2019/06/02 7:00:00' and '2019/06/03 06:59:59'
How can I code the where condition?
id | create_date | judge |
-----+---------------------+---------+
1 | 2019-06-02 8:00:00 | ok |
2 | 2019-06-02 9:00:00 | ok |
3 | 2019-06-02 10:00:00 | ok |
4 | 2019-06-02 11:00:00 | ok |
5 | 2019-06-02 15:00:00 | ok |
6 | 2019-06-03 4:00:00 | ok |
7 | 2019-06-03 5:00:00 | ok |
8 | 2019-06-03 8:00:00 | ok |
9 | 2019-06-03 9:00:00 | ok |
10 | 2019-06-03 9:00:00 | fail |
I've tried below but the result is not as expected.
SELECT COUNT(*),DAY(create_date)
FROM mytable
WHERE judge = 'ok' and MONTH(create_date) = '6' and YEAR(create_date) = '2019' and TIME(create_date) > '07:00:00'
Group by DAY(create_date) order by DAY(create_date) ASC
Expected results
COUNT(*) | DAY(create_date) |
-----------+---------------------+
7 | 2 | (from id 1 to 7)
2 | 3 | (from id 8 and 9)
You could subtract seven hours from each date, truncate them to show the date only and then group them:
SELECT DATE(DATE_SUB(create_date, INTERVAL 7 HOUR)), COUNT(*)
FROM mytable
-- Where clause if you need it...
GROUP BY DATE(DATE_SUB(create_date, INTERVAL 7 HOUR))
Just subtract 7 hours for the aggregation and the date/time comparisons:
SELECT DATE(create_date - interval 7 hour) as dte, COUNT(*)
FROM mytable
WHERE judge = 'ok' and
create_date >= '2019-06-01 07:00:00' AND
create_date < '2019-07-01 07:00:00'
GROUP BY DATE(create_date - interval 7 hour)
ORDER BY dte;
Try this-
SELECT
CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE),
COUNT(*)
FROM YOUR_TABLE
GROUP BY CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE)

CROSS JOIN of list of dates and another table

I have a query for getting data from a certain table by date range and grouping by week. My CROSS JOIN intends to fill in a default value for each week where there are no results for the date range.
I can then execute this query.
SELECT
SUM(invoice.amount) AS "invoice.amount",
CONCAT(DATE_FORMAT(invoice.updated_at, '%b %d'), ' - ', DATE_FORMAT(DATE_ADD(invoice.updated_at, INTERVAL 7 DAY), '%b %d')) AS "invoice.updated_at"
FROM invoice
CROSS JOIN (
SELECT selected_date
FROM (
SELECT ADDDATE('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date
FROM
(SELECT 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(SELECT 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(SELECT 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(SELECT 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(SELECT 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4
) v
WHERE selected_date BETWEEN '2018-01-01' AND '2018-01-31'
GROUP BY selected_date, YEAR(selected_date), WEEK(selected_date)
) calendar
WHERE invoice.updated_at >= '2018-01-01'
AND invoice.updated_at <= '2018-01-31'
AND invoice.status = "PAID"
GROUP BY calendar.selected_date, invoice.id, invoice.amount, YEAR(invoice.updated_at), WEEK(invoice.updated_at)
Assume I have these records in the database:
+----+------------+------------+------------+
| id | amount | status | updated_at |
+----+------------+------------+------------+
| 1 | 1000 | PAID | 2018-01-01 |
| 2 | 2000 | PAID | 2018-01-01 |
| 3 | 100 | PAID | 2018-01-07 |
| 4 | 50 | PAID | 2018-01-11 |
+----+------------+------------+------------+
I expect to see these results, one record for every week of January:
+--------+-------------------+
| amount | updated_at |
+--------+-------------------+
| 3100 | Jan 1 - Jan 7 |
| 50 | Jan 8 - Jan 15 |
| 0 | Jan 16 - Jan 22 |
| 0 | Jan 23 - Jan 30 |
| 0 | Jan 31 - Jan 31 |
+--------+-------------------+
However, I get like 50 of these random duplicated results, the least of which contains the joined filler weeks since there are no 0 amounts:
+--------+----------------+
| amount | updated_at |
+--------+----------------+
| 1000 | Jan 1 - Jan 7 |
| 2000 | Jan 1 - Jan 7 |
| 100 | Jan 1 - Jan 7 |
| 50 | Jan 8 - Jan 15 |
| 1000 | Jan 1 - Jan 7 |
| 1000 | Jan 1 - Jan 7 |
| 2000 | Jan 1 - Jan 7 |
| 2000 | Jan 1 - Jan 7 |
| 100 | Jan 1 - Jan 7 |
| 50 | Jan 8 - Jan 15 |
| 100 | Jan 1 - Jan 7 |
| 50 | Jan 8 - Jan 15 |
| ... | ... |
| ... | ... |
| ... | ... |
+--------+----------------+
What gives?
GROUP BY calendar.selected_date, invoice.id, invoice.amount,
You have too many columns specified in the group by, particularly invoice.amount
Instead, try with:
GROUP BY
CONCAT(DATE_FORMAT(invoice.updated_at, '%b %d'), ' - ', DATE_FORMAT(DATE_ADD(invoice.updated_at, INTERVAL 7 DAY), '%b %d'))
I cannot be sure, but I think your date range needs adjustment as well, the following will guarantee you get everything for January 2018:
WHERE invoice.updated_at >= '2018-01-01'
AND invoice.updated_at < '2018-02-01'

mysql Return amount of order for hour with on colum day of month

I have a mysql db which I use to return amounts of orders by hour in a specific day. I use this SELECT statement for that.
select
hour(datains),sum(valore)
from
ordini
where (stato=10 or stato = 1 ) and DATE(datains) = DATE_SUB(CONCAT(CURDATE(), ' 00:00:00'), INTERVAL 0 DAY)
group by hour(datains)
order by
id DESC
It returns:
+--------------+---------------+
| hour datains | valore |
| 12 | 34 |
| 11 | 56 |
| 10 | 134 |
+-------------------------------
Now I need to have columns for a certain number of days, like this.
+--------------+---------------+--------------+--------------+
| hour datains | 01-01-2014 | 02-01-2014 | 03-01-2014 |
| 12 | 34 | 34 | 77 |
| 11 | 56 | 0 | 128 |
| 10 | 134 | 66 | 12 |
+------------------------------+-----------------------------+
Is this possible?
It seems you have a table ordini with columns datains, valore, and stato.
Perhaps you can try this query to generate hour-by-hour aggregates for a three days' worth of recent sales, but not including today.
SELECT DATE_FORMAT(datains, '%Y-%m-%d %H:00') AS hour,
SUM(valore) AS valore
FROM ordini
WHERE (stato = 1 OR stato = 10)
AND datains >= CURRENT_DATE() - INTERVAL 3 DAY
AND datains < CURRENT_DATE
GROUP BY DATE_FORMAT(datains, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(datains, '%Y-%m-%d %H:00')
This will give you a result set with one row for each hour of the three days, for example:
2014-01-01 10:00 456
2014-01-01 11:00 123
2014-01-02 10:00 28
2014-01-02 11:00 350
2014-01-02 12:00 100
2014-01-02 13:00 17
2014-01-03 10:00 321
2014-01-03 11:00 432
2014-01-03 12:00 88
2014-01-03 13:00 12
That's the data summary you have requested, but formatted row-by-row. Your next step is to figure out an appropriate technique to pivot that result set, formatting it so some rows become columns.
It happens that I have just written a post on this very topic. It is here:
http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

Group dates by only month and year

Assuming I have a table like the following:
id | assignment | duedate
1 | Math | 2012-01-01
2 | History | 2012-02-02
3 | Science | 2012-01-01
4 | Government | 2012-02-01
5 | Government | 2013-01-13
6 | History | 2013-03-13
Is it possible to make some sql query such that I get a grouping of all the dates by month and year? Is there some possibility that I could get a sorted result of:
duedatemonth | count
January 2012 | 2
Feburary 2012 | 2
January 2013 | 1
March 2013 | 1
I know you can GROUP BY duedate, but that only groups those with the same month, day, and year instead of just month and year.
Would it be then possible to even further group it such that it factors in "assignment" to obtain a resulting table of
id | duedatemonth | count
1 | January 2012 | 2
3 | January 2012 | 2
2 | Feburary 2012 | 2
4 | Feburary 2012 | 2
5 | January 2013 | 1
6 | March 2013 | 1
try this
SELECT DATE_FORMAT(duedate,'%M %Y') duedatemonth, COUNT(*) count
FROM Table1
GROUP BY year(duedate), MONTH(duedate)
DEMO HERE
will output this:
DUEDATEMONTH COUNT
January 2012 2
February 2012 2
January 2013 1
March 2013 1
Use the string functions YEAR and MONTH.
SELECT YEAR(duedate), MONTH(duedate), COUNT(*)
FROM sparkles
GROUP BY YEAR(duedate), MONTH(duedate)
Use MONTHNAME or DATE_FORMAT to get the name of the month.
You can use this query.
SELECT DATE_FORMAT(duedate, '%M %Y') duedatemonth, COUNT(1) `count`
FROM Tbl
GROUP BY DATE_FORMAT(duedate, '%M %Y')