I'm writing a software to manage worked hours. For each day could be different activities.
I wish to extract the worked hours for each worker in the last 7 days.
Sample rows
+----+------------+-------+----------+----------+
| id | date | order | operator | duration |
+----+------------+-------+----------+----------+
| 37 | 2016-06-12 | 27 | 1 | 180 |
| 38 | 2016-06-12 | 28 | 3 | 390 |
| 39 | 2016-06-12 | 27 | 1 | 480 |
| 40 | 2016-06-04 | 21 | 2 | 120 |
| 41 | 2016-05-07 | 27 | 1 | 90 |
| 42 | 2016-06-07 | 27 | 1 | 150 |
+----+------------+-------+----------+----------+
The Query
SELECT SUM(`duration`) as `hours_per_day`
FROM `sheets`
WHERE `operator` = 1 AND `date` = DATE_ADD(CURDATE(), INTERVAL -7 DAY)
ORDER BY `date` DESC
Expected result:
+------------------------+--------+--------+--------+--------+--------+--------+--------+
| Operator: Avareage Joe |
+------------------------+--------+--------+--------+--------+--------+--------+--------+
| Day: | 01 jul | 02 jul | 03 jul | O4 jul | 05 jul | 06 jul | 07 jul |
| Hours: | 8 | 7 | 9 | 8 | 9 | 8 | 6 |
+------------------------+--------+--------+--------+--------+--------+--------+--------+
SQL Fiddle Demo
SELECT
CURDATE(),
SUM(CASE WHEN CURDATE() = `date` THEN `duration` ELSE 0 END) as today ,
SUM(CASE WHEN CURDATE() - INTERVAL 1 DAY = `date` THEN `duration` ELSE 0 END) as yesterday,
SUM(CASE WHEN CURDATE() - INTERVAL 2 DAY = `date` THEN `duration` ELSE 0 END) as `today - 2`,
SUM(CASE WHEN CURDATE() - INTERVAL 3 DAY = `date` THEN `duration` ELSE 0 END) as `today - 3`,
SUM(CASE WHEN CURDATE() - INTERVAL 4 DAY = `date` THEN `duration` ELSE 0 END) as `today - 4`,
SUM(CASE WHEN CURDATE() - INTERVAL 5 DAY = `date` THEN `duration` ELSE 0 END) as `today - 5`,
SUM(CASE WHEN CURDATE() - INTERVAL 6 DAY = `date` THEN `duration` ELSE 0 END) as `today - 6`
FROM `work`
where operator = 1
OUTPUT
Let's try it like this:
SELECT operator, `date`, (SUM(duration) / 60) as hours_per_day
FROM sheets
WHERE `date` > NOW() - INTERVAL 7 DAY
GROUP BY operator, date
ORDER BY operator, date
Just look at the WHERE clause first. We exclude everything that is more than 7 days old.
Then we use GROUP BY to take all the remaining rows and group them by operator and date, so you basically get small sub-results that you can work with.
I retained your SUM(duration) operation, which now calculates sums for every one of these little sub-results. I just added the division, because obviously you're storing minutes, not hours.
Finally we use ORDER BY to make sure the result doesn't look like a big mess.
Your result should look something like this:
operator | date | hours_per_day
---------------------------------------
1 | 2016-07-01 | 4
1 | 2016-07-02 | 6
1 | 2016-07-03 | 6
2 | 2016-07-01 | 8
2 | 2016-07-02 | 7
Related
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)
My sale Table
ID amount created_at
48 10 2018-10-15 10:57:24
49 20 2018-10-16 10:58:14
50 25 2018-10-22 14:07:31
51 24 2018-10-24 12:13:15
52 36 2018-10-24 12:13:21
53 40 2018-10-30 09:46:37
54 40 2018-10-28 09:46:37
55 40 2018-11-1 09:46:37
56 40 2018-11-2 09:46:37
57 40 2018-11-2 09:46:37
58 40 2018-11-2 09:46:37
59 40 2018-11-2 09:46:37
60 40 2018-11-2 09:46:37
My qyery
SELECT Date(created_at),
Count(*)
FROM sale
WHERE Date(created_at) BETWEEN ( Now() - INTERVAL 7 day ) AND Now()
GROUP BY Date(created_at)
My result
date(created_at) count
2018-10-28 12:13:15 1
2018-10-1 09:46:37 1
2018-10-2 09:46:37 5
Suppose Week Start from 2018-10-28 and i need result like below if there is no record of particular day then it will 0.
day count
mon 1
tue 0
wed 0
thu 1
fri 5
sat 0
sun 0
Firstly, you can create a Master Table representing all the Abbreviated Weekday name(s).
Now, we can use Date_format() function with %a specifier to get the abbreviated weekday name for a particular created_at date. We can use this as our Left Join condition.
Left Join allows us to consider all the Weekdays, even if there is no matching created_at for a particular day.
Count(*) will not work here, as it counts all the rows in a group. However, we don't want to count the rows where there is no matching rows. So, we use Count(created_at), as it will ignore the null values.
Finally, query is made sargable by removing Date() function usage in the Where clause.
You will need to use a master table for week days.
Query:
SELECT week_table.day_order,
week_table.wkday AS `day`,
Count(created_at) AS `count`
FROM
(
SELECT 'Mon' AS wkday, 1 AS day_order UNION ALL
SELECT 'Tue', 2 UNION ALL
SELECT 'Wed', 3 UNION ALL
SELECT 'Thu', 4 UNION ALL
SELECT 'Fri', 5 UNION ALL
SELECT 'Sat', 6 UNION ALL
SELECT 'Sun', 7
) AS week_table
LEFT JOIN sale AS s
ON DATE_FORMAT(created_at, '%a') = week_table.wkday AND
created_at >= ( CURDATE() - INTERVAL 7 day ) AND
created_at < ( CURDATE() + INTERVAL 1 day )
GROUP BY week_table.day_order, week_table.wkday
ORDER BY week_table.day_order;
Result
| day_order | day | count |
| --------- | --- | ----- |
| 1 | Mon | 0 |
| 2 | Tue | 1 |
| 3 | Wed | 0 |
| 4 | Thu | 1 |
| 5 | Fri | 5 |
| 6 | Sat | 0 |
| 7 | Sun | 1 |
View on DB Fiddle
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.
I have an mysql statement as below
SELECT CASE
WHEN HOUR(created_at) BETWEEN 0 AND 11 THEN 'Morning'
WHEN HOUR(created_at) BETWEEN 12 AND 15 THEN 'Afternoon'
WHEN HOUR(created_at) BETWEEN 16 AND 18 THEN 'Evening'
WHEN HOUR(created_at) BETWEEN 19 AND 24 THEN 'Night'
END AS session,
SUM(total) AS `total` FROM `orders` WHERE (purchase_date between '2014-05-01' and '2014-05-30')
GROUP BY CASE
WHEN HOUR(created_at) BETWEEN 0 AND 11 THEN 1
WHEN HOUR(created_at) BETWEEN 12 AND 16 THEN 2
WHEN HOUR(created_at) BETWEEN 17 AND 18 THEN 3
WHEN HOUR(created_at) BETWEEN 19 AND 24 THEN 4
END;
I am getting an output like this
+------------+------------+
| session | total |
+------------+------------+
| Morning | 47083.21 |
| Afternoon | 1124804.51 |
| Evening | 165643.34 |
| Night | 1690492.01 |
+------------+------------+
But when there are no entries for morning then the output is missing the Morning row in results but I want an row with morning but total as 0.
please help me how to achieve the same
Expected output
+------------+------------+
| session | total |
+------------+------------+
| Morning | 0 |
| Afternoon | 14804.51 |
| Evening | 16643.34 |
| Night | 19492.01 |
+------------+------------+
actual output is without morning row
Actual output
+------------+------------+
| session | total |
+------------+------------+
| Afternoon | 1124804.51 |
| Evening | 165643.34 |
| Night | 1690492.01 |
+------------+------------+
I would greatly appreciate if Any kind of help or hint is given to solve this problem
Add create table cal (hours int not null). Fill it with 0 to 23 (there is no 24 hour?). Then do
SELECT CASE
WHEN cal.hours BETWEEN 0 AND 11 THEN 'Morning'
WHEN cal.hours BETWEEN 12 AND 15 THEN 'Afternoon'
WHEN cal.hours BETWEEN 16 AND 18 THEN 'Evening'
WHEN cal.hours BETWEEN 19 AND 24 THEN 'Night'
END AS session
,sum(coalesce(total, 0)) from
(select created_at, total from orders
where purchase_date between '2014-05-01' and '2014-05-30') T1
right outer join cal on (cal.hours = hour(T1.created_at))
group by CASE
WHEN cal.hours BETWEEN 0 AND 11 THEN 'Morning'
WHEN cal.hours BETWEEN 12 AND 15 THEN 'Afternoon'
WHEN cal.hours BETWEEN 16 AND 18 THEN 'Evening'
WHEN cal.hours BETWEEN 19 AND 24 THEN 'Night' END;
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/