I want to return the most recent action in the timetable that has taken place, grouped by channel- but ordered by enum. The timetable can have 'WEEKDAY' options, or specific days ('WED'). The specific days need to take precedence over the 'WEEKDAY' option.
CREATE TABLE `heatingtimetable` (
`id` int(11) NOT NULL,
`channel` enum('CENTRALHEATING','HOTWATER') NOT NULL,
`command` enum('ON','OFF') NOT NULL DEFAULT 'OFF',
`thetime` time NOT NULL,
`day` enum('SUN','MON','TUE','WED','THU','FRI','SAT','WEEKDAY','WEEKEND','HOLIDAY') NOT NULL
);
INSERT INTO `heatingtimetable` (`id`, `channel`, `command`, `thetime`, `day`) VALUES
(1, 'CENTRALHEATING', 'ON', '08:00:00', 'WEEKDAY'),
(2, 'CENTRALHEATING', 'OFF', '10:00:00', 'WEEKDAY'),
(13, 'CENTRALHEATING', 'ON', '07:00:00', 'WED'),
(14, 'CENTRALHEATING', 'OFF', '9:00:00', 'WED');
https://www.db-fiddle.com/#&togetherjs=NEuAL2we4r
I can get back the most recent- but not a custom/enum order as the group by happens first- the most recent time always comes up. Assuming that is it 10.15am on a Wednesday- this brings back WEEKDAY, but needs to bring back 'WED 9am' row 14.
SELECT h.channel, command, h.thetime, day FROM
(SELECT channel, MAX(thetime) as thetime
FROM `heatingtimetable`
where thetime < '10:15'
AND
(DAYOFWEEK(CURDATE()) >= 2 AND DAYOFWEEK(CURDATE()) <=6 AND day = 'WEEKDAY') || (DAYOFWEEK(CURDATE()) = day)
group by channel) as l
INNER JOIN heatingtimetable h on h.channel = l.channel and h.thetime = l.thetime```
Rethink structure if you can
I managed a solution here below, but wow it isn't pretty. I would really really recommend thinking about structuring this differently so that comparing current day/time to the entries is easier. Hopefully you have control of the table structure and such.
Think first about what query you would like to use. Then you can work from there to find the best table structure to suit such queries. A comparison of datetimes would be a lot friendlier since you can do math that includes both the date and the time. Also mixing different types like WED, FRI, WEEKDAY, WEEKEND and having them as enums with no natural way of ranking them is problematic. That's why this query below is gigantic. If you could get rid of the different types being mixed that would help. If you can't do that, you could perhaps add a new column that helps you rank WED higher than WEEKDAY.
We need a way to rank these
I understand about the WED vs WEEKDAY precedence now. In that case, we can do this by making our own ranking value. We want to rank a matching DAYOFWEEK as higher than a matching WEEKDAY/WEEKEND, etc. And then you also want to rank by time with lower priority than those. So we can order by these 3 different things in that order.
CASE WHEN/THEN/ELSE comes in handy for conditionals.
There are MANY situations to consider here
By the way, your query does not return the most recent like you said in some situations. If today is Saturday, then DAYOFWEEK(CURDATE()) will be 7 and you will have no rows returned. You probably want row 2 to be returned since it would be the most recent. Your query also didn't consider WEEKENDs in any way.
What if the current day matches the table entry's day, but the current time is after the table entry's time? We should not count that entry as matching the day. So we have to include a time check in these ranking values.
Adding on to that-
What if today is Friday and that doesn't match the day of any entries... but you have one entry for WEEKDAY and one for THU? Which would be preferred as the most recent? I assume you would want THU to be preferred to WEEKDAY like if it is the same day. And if the choice was between WED and WEEKDAY, then WEEKDAY would be preferred because Thursday is a more recent weekday than WED.
It gets quite complicated.
CURTIME()
Also, I noticed you specified the current day in SQL using CURDATE(), but for the time you just entered a constant value '10:15'. I'm not sure where that comes from, but if you want to do that in SQL also, it would just be - CURTIME(). You can also get both the date and time together with NOW().
Set up the scoring
I created the following ranking values:
day_score1 - This will be 1 if the day of the week matches exactly (like WED/Wednesday) and the table entry time is before the current time, otherwise 0.
day_score2 - This will be 1 if the current day matches WEEKDAY/WEEKEND and the table entry time is before the current time, otherwise 0
day_score3 - This value will be used to go backwards in days and the more recent the day, the higher the score. We have to use subraction here, and we also have to wrap around. A big problem here is that there is no way to get a weekday index number from your day values such as WED. DAYOFWEEK() will take a date and return the weekday index number, but there is no reverse. So we'll have to do a check on each possible day of the week. We won't need to consider the time in this one since we know we are going backwards at least one day.
And if we still want to have WED have higher precedence than WEEKDAY in this situation where we have to look backwards, there have to be even more lines for scoring.
thetime - And lastly I have ordering also by thetime descending because that way if we have to fall back on a previous day and there is more than one entry with that day, it will put the latest one first.
I see you have HOLIDAY there also but I ignored that. This is plenty complex already and I will not bite on that.
Here is my DB Fiddle- https://www.db-fiddle.com/f/wgSzEiWrDgAJuc88de1Fwq/0
CREATE TABLE and INSERTions below-
CREATE TABLE `heatingtimetable` (
`id` int(11) NOT NULL,
`channel` enum('CENTRALHEATING','HOTWATER') NOT NULL,
`command` enum('ON','OFF') NOT NULL DEFAULT 'OFF',
`thetime` time NOT NULL,
`day` enum('SUN','MON','TUE','WED','THU','FRI','SAT','WEEKDAY','WEEKEND','HOLIDAY') NOT NULL
);
INSERT INTO `heatingtimetable` (`id`, `channel`, `command`, `thetime`, `day`) VALUES
(1, 'CENTRALHEATING', 'ON', '08:00:00', 'WEEKDAY'),
(2, 'CENTRALHEATING', 'OFF', '10:00:00', 'WEEKDAY'),
(3, 'CENTRALHEATING', 'OFF', '22:00:00', 'WEEKDAY'),
(4, 'CENTRALHEATING', 'ON', '05:00:00', 'MON'),
(5, 'CENTRALHEATING', 'OFF', '16:00:00', 'TUE'),
(6, 'CENTRALHEATING', 'OFF', '23:00:00', 'THU'),
(7, 'CENTRALHEATING', 'OFF', '6:00:00', 'THU'),
(8, 'CENTRALHEATING', 'ON', '07:00:00', 'FRI'),
(9, 'CENTRALHEATING', 'OFF', '21:00:00', 'FRI'),
(13, 'CENTRALHEATING', 'ON', '07:00:00', 'WED'),
(14, 'CENTRALHEATING', 'OFF', '9:00:00', 'WED');
And this gigantic query-
SELECT
channel, command, thetime, day,
CASE WHEN (DAYOFWEEK(CURDATE()) = day AND thetime <= CURTIME()) THEN 1 ELSE 0 END AS day_score1,
CASE WHEN
(
(
(DAYOFWEEK(CURDATE()) >= 2 AND DAYOFWEEK(CURDATE()) <= 6 AND day = 'WEEKDAY')
OR
((DAYOFWEEK(CURDATE()) = 1 OR DAYOFWEEK(CURDATE()) = 7) AND day = 'WEEKEND')
)
AND thetime <= CURTIME()
) THEN 1 ELSE 0 END as day_score2,
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'SAT' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'SUN' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'MON' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'TUE' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'WED' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'THU' THEN 12 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'FRI' THEN 12 ELSE 0 END
+
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'FRI' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'SAT' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'SUN' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'MON' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'TUE' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'WED' THEN 10 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'THU' THEN 10 ELSE 0 END
+
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'THU' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'FRI' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'SAT' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'SUN' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'MON' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'TUE' THEN 8 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'WED' THEN 8 ELSE 0 END
+
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'WED' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'THU' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'FRI' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'SAT' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'SUN' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'MON' THEN 6 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'TUE' THEN 6 ELSE 0 END
+
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'TUE' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'WED' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'THU' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'FRI' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'SAT' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'SUN' THEN 4 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'MON' THEN 4 ELSE 0 END
+
CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 6 AND day = 'MON' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 5 AND day = 'TUE' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 4 AND day = 'WED' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 3 AND day = 'THU' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 2 AND day = 'FRI' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 1 AND day = 'SAT' THEN 2 ELSE 0 END
+ CASE WHEN (7 - DAYOFWEEK(CURDATE())) = 0 AND day = 'SUN' THEN 2 ELSE 0 END
+
CASE
WHEN (7 - DAYOFWEEK(CURDATE())) <= 4 AND day = 'WEEKDAY' THEN 11
WHEN (7 - DAYOFWEEK(CURDATE())) >= 5 AND day = 'WEEKEND' THEN 11
WHEN (7 - DAYOFWEEK(CURDATE()) >= 6 OR 7 - DAYOFWEEK(CURDATE()) <= 3) AND day = 'WEEKDAY' THEN 9
WHEN (7 - DAYOFWEEK(CURDATE()) = 5 OR 7 - DAYOFWEEK(CURDATE()) = 4) AND day = 'WEEKEND' THEN 9
WHEN (7 - DAYOFWEEK(CURDATE()) >= 5 OR 7 - DAYOFWEEK(CURDATE()) <= 2) AND day = 'WEEKDAY' THEN 7
WHEN (7 - DAYOFWEEK(CURDATE()) = 4 OR 7 - DAYOFWEEK(CURDATE()) = 3) AND day = 'WEEKEND' THEN 7
WHEN (7 - DAYOFWEEK(CURDATE()) >= 4 OR 7 - DAYOFWEEK(CURDATE()) <= 1) AND day = 'WEEKDAY' THEN 5
WHEN (7 - DAYOFWEEK(CURDATE()) = 3 OR 7 - DAYOFWEEK(CURDATE()) = 2) AND day = 'WEEKEND' THEN 5
WHEN (7 - DAYOFWEEK(CURDATE()) >= 3 OR 7 - DAYOFWEEK(CURDATE()) <= 0) AND day = 'WEEKDAY' THEN 3
WHEN (7 - DAYOFWEEK(CURDATE()) = 2 OR 7 - DAYOFWEEK(CURDATE()) = 1) AND day = 'WEEKEND' THEN 3
WHEN (7 - DAYOFWEEK(CURDATE()) >= 2) AND day = 'WEEKDAY' THEN 1
WHEN (7 - DAYOFWEEK(CURDATE()) <= 1) AND day = 'WEEKEND' THEN 1
ELSE 0
END
AS day_score3
FROM heatingtimetable
ORDER BY day_score1 DESC, day_score2 DESC, day_score3 DESC, thetime DESC
My result right now is below. It's Friday and current time at DB-Fiddle is showing as 20:41:40.
channel
command
thetime
day
day_score1
day_score2
day_score3
CENTRALHEATING
ON
07:00:00
FRI
1
0
0
CENTRALHEATING
OFF
10:00:00
WEEKDAY
0
1
11
CENTRALHEATING
ON
08:00:00
WEEKDAY
0
1
11
CENTRALHEATING
OFF
23:00:00
THU
0
0
12
CENTRALHEATING
OFF
06:00:00
THU
0
0
12
CENTRALHEATING
OFF
22:00:00
WEEKDAY
0
0
11
CENTRALHEATING
OFF
09:00:00
WED
0
0
10
CENTRALHEATING
ON
07:00:00
WED
0
0
10
CENTRALHEATING
OFF
16:00:00
TUE
0
0
8
CENTRALHEATING
ON
05:00:00
MON
0
0
6
CENTRALHEATING
OFF
21:00:00
FRI
0
0
0
I have table sql like this:
This my query for count tgl:
SELECT count( tgl ) AS total, absen.id
FROM absen
WHERE absen.status = 'm'
GROUP BY absen.id
So I want group by absen.id and absen.tgl
How to group by week from Friday to Thursday?
2016-01-08 is friday and 2016-01-15 is thursday.
Bellow query can bring the result you want, but i think you defined the wrong end date, because in your example from 2015-01-08 up to 2015-01-15 its 8 day and one week has 7 days.
select
count( tgl ) AS total,
absen.id,
CASE WHEN (weekday(tgl)<=3) THEN date(tgl + INTERVAL (3-weekday(tgl)) DAY)
ELSE date(tgl + INTERVAL (3+7-weekday(tgl)) DAY)
END as week_days
FROM absen
WHERE status = 'm'
GROUP BY id,week_days
here is the fiddle fiddle
Query Description:
mysql weekday array numbers:
$weekArr = array(
'Monday' => 0,
'Tuesday' => 1,
'Wednesday' => 2,
'Thursday' => 3,
'Friday' => 4,
'Saturday' => 5,
'Sunday' => 6);
So now suppose today is Tuesday and date is 2016-01-12, now let's count from today towards the start date in our table which is 2016-01-07 and it match with Thursday of past week, so according to the weekday array number its weekday(2016-01-07) == 3 so it goes to the WHEN part of our query, and query will select something like this CASE WHEN (weekday('2016-01-07') <= 3) THEN date('2016-01-07' + INTERVAL(3-3)) that is equal to SELECT '2016-01-07' and so on for others.
I just found how to get this by trouble shooting on excel by using this WEEK('date' + INTERVAL 3 DAY, 3)
I'm trying to return 2 columns: Day of the week and number of occurrences in that day. How would I return the number of occurrences for each day?
SELECT case CAST(strftime('%w', HireDate) AS INTEGER)
WHEN 0 then 'Sunday'
WHEN 1 then 'Monday'
WHEN 2 then 'Tuesday'
WHEN 3 then 'Wednesday'
WHEN 4 then 'Thursday'
WHEN 5 then 'Friday'
ELSE 'Saturday' END AS 'Day of week', AS 'Hired'
FROM Employee;
Just rewrite case on group by clause:
SELECT
( case CAST(strftime('%w', HireDate) AS INTEGER)
WHEN 0 then 'Sunday'
WHEN 1 then 'Monday'
WHEN 2 then 'Tuesday'
WHEN 3 then 'Wednesday'
WHEN 4 then 'Thursday'
WHEN 5 then 'Friday'
ELSE 'Saturday' END ) AS 'Day of week',
count(*) AS 'Hired' --<--here
FROM
Employee
GROUP BY --<--here
( case CAST(strftime('%w', HireDate) AS INTEGER)
WHEN 0 then 'Sunday'
WHEN 1 then 'Monday'
WHEN 2 then 'Tuesday'
WHEN 3 then 'Wednesday'
WHEN 4 then 'Thursday'
WHEN 5 then 'Friday'
ELSE 'Saturday' END );
i have a mysql query
SELECT CONCAT('document',LEFT(cases.id,26)) AS 'id',
cases.date_modified,
cases.date_modified,
'67421f0a-3f44-1093-1ab6-51750b508349' AS 'assigned_user','1','1','0',
'complete documentation in 4 Days' AS 'name',
'Not Started',
cases.date_modified,
DATE_ADD(cases.date_modified,INTERVAL 4 DAY) AS 'due date','Cases',cases.id,
'P1','Complete Operations Documentation' AS 'description'
FROM cases
INNER JOIN cases_cstm
ON ( cases.id = cases_cstm.id_c)
WHERE cases.`assigned_user_id` = '67421f0a-3f44-1093-1ab6-51750b508349'
AND cases.`status` = 'Start Administration'
AND `cases_cstm`.`file_location` = 'Operations'
ORDER BY `date_modified` DESC
LIMIT 1;
but the problem is on tuesday it will come up for saterday and wednesday it will come up for sunday
and thursday or friday the people will have 2 days les to work?
how can i check that if a weekend fals in the due date to add 2 days?
regards
Try this expression:
(case when weekday(cases.date_modified) = 0
then DATE_ADD(cases.date_modified, INTERVAL 4 DAY)
else DATE_ADD(cases.date_modified, INTERVAL 6 DAY)
end) as 'due date'
It just tests for the day of the week and then adds the appropriate duration to pass the weekend.
The DAYOFWEEK function will return 1 for Sunday, 2 for Monday, 3 for Tuesday, ... through 7 for Saturday. You can use it in a condition like so:
DATE_ADD(cases.date_modified,
CASE DAYOFWEEK(cases.date_modified)
WHEN 3 THEN INTERVAL 6 DAY
WHEN 4 THEN INTERVAL 5 DAY
ELSE INTERVAL 4 DAY
END AS 'due date'
The example above adds 6 days for a Tuesday (going to the next Monday), 5 days for a Wednesday (going to the next Monday), and 4 for all others. This is different than what you asked for (where Wednesday goes to the next Tuesday rather than Monday), but it may be more appropriate. You can tweak as necessary.