我有一张表格如下:
CREATE TABLE `zonetimes` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`zone_id` int(10) unsigned NOT NULL,
`active_from_day` tinyint(1) unsigned NOT NULL DEFAULT '2',
`active_to_day` tinyint(1) unsigned NOT NULL DEFAULT '2',
`active_from` time NOT NULL,
`active_to` time NOT NULL
PRIMARY KEY (`id`)
) ENGINE=MyISAM ;
因此,用户可以在特定日期和时间开始添加时间条目,并在特定日期和时间结束,例如: 周一08:00至周五18:00或周四15:00至周二15:00(注意周末的交叉)。
我需要查询这些数据并确定某个区域当前是否处于活动状态(NOW(),DAYOFWEEK()等)......这结果非常棘手。
如果我没有重叠,例如:从'星期三晚上8点到星期二凌晨4点'或者从'星期四下午4点到星期二下午4点',这对BETWEEN来说很容易。
此外,需要允许用户添加整个星期,例如:星期一早上8点 - 星期一早上8点(这应该很容易,例如:where(active_from_day = active_to_day AND active_from = active_to)OR ..
有什么想法吗?
注意:我在Timespan - Check for weekday and time of day in mysql找到了类似的问题,但没有得到答案。其中一个建议是将每一天存储为一个单独的行。我宁愿存储多天的一段时间。
Terje D的查询完美无缺。一些测试场景:
+---------------------+-----------------+---------------+-------------+-----------+------------+
| NOW( ) | active_from_day | active_to_day | active_from | active_to | active_now |
+---------------------+-----------------+---------------+-------------+-----------+------------+
| 2012-10-31 23:41:55 | 1 | 4 | 08:00:00 | 23:40:00 | 0 |
| 2012-10-31 23:42:25 | 1 | 4 | 08:00:00 | 23:45:00 | 1 |
| 2012-10-31 23:42:57 | 4 | 1 | 08:00:00 | 09:45:00 | 1 |
| 2012-10-31 23:43:36 | 4 | 4 | 23:00:00 | 09:45:00 | 1 |
| 2012-10-31 23:44:10 | 5 | 4 | 00:00:00 | 23:44:00 | 0 |
| 2012-10-31 23:44:27 | 5 | 4 | 00:00:00 | 23:45:00 | 1 |
| 2012-10-31 23:45:14 | 2 | 2 | 00:00:00 | 00:00:00 | 0 |
上面的结果是通过运行几次Terje的查询生成的:
SELECT NOW( ) , active_from_day, active_to_day, active_from, active_to, (
DAYOFWEEK( NOW( ) ) > active_from_day
OR DAYOFWEEK( NOW( ) ) = active_from_day
AND TIME( NOW( ) ) > active_from
)
AND (
DAYOFWEEK( NOW( ) ) < active_to_day
OR DAYOFWEEK( NOW( ) ) = active_to_day
AND TIME( NOW( ) ) < active_to
)
OR (
active_from_day > active_to_day
OR active_from_day = active_to_day
AND active_from > active_to
)
AND (
(
DAYOFWEEK( NOW( ) ) > active_from_day
OR DAYOFWEEK( NOW( ) ) = active_from_day
AND TIME( NOW( ) ) > active_from
)
OR (
DAYOFWEEK( NOW( ) ) < active_to_day
OR DAYOFWEEK( NOW( ) ) = active_to_day
AND TIME( NOW( ) ) < active_to
)
) AS active_now FROM zonetimes
答案 0 :(得分:1)
这个逻辑应该是:
IF active_to >= active_from AND NOW() BETWEEN active_from AND active_to
OR active_to < active_from AND NOW NOT BETWEEN active_to AND active_from
即
IF (DAYOFWEEK(NOW()) > active_from_day
OR DAYOFWEEK(NOW()) = active_from_day AND TIME(NOW()) > active_from)
AND (DAYOFWEEK(NOW()) < active_to_day
OR DAYOFWEEK(NOW()) = active_to_day AND TIME(NOW()) < active_to)
OR (active_from_day > active_to_day
OR active_from_day = active_to_day AND active_from > active_to)
AND ((DAYOFWEEK(NOW()) > active_from_day
OR DAYOFWEEK(NOW()) = active_from_day AND TIME(NOW()) > active_from)
OR (DAYOFWEEK(NOW()) < active_to_day
OR DAYOFWEEK(NOW()) = active_to_day AND TIME(NOW()) < active_to))
(如果时间是在开始之后和结束之前,则区域处于活动状态, 或者如果开始&gt;结束,时间是在开始之后或结束之前)
答案 1 :(得分:0)
报废,看看Terje D的回答。
一种可能的方法(导致查询混乱)使用Skpd的想法:
我创建了一个开始日(在周一到周日的tinyint 0到6),然后是一个开始时间(从午夜开始的int分钟)和一个范围(int minutes)。因此,区域处于活动状态的时间段介于(day_of_week + start)和(day_of_week + start + range)之间。当queryig我查询上周的时间段时,以及本周的时间段来捕获两者。我很确定有更优雅的方法,但我认为它适用于所有组合。并将存储空间保持在最低限度
CREATE TABLE `azonetimes` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`zone_id` int(10) unsigned NOT NULL,
`day_of_week` tinyint(1) unsigned NOT NULL DEFAULT '2',
`start` int(11) unsigned NOT NULL DEFAULT '0',
`range` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM ;
查询:(周三07:50 am)
SELECT
azonetimes.*, (
NOW()
#Check for last week
BETWEEN
(FROM_UNIXTIME(UNIX_TIMESTAMP(DATE(CURDATE() - INTERVAL WEEKDAY( CURDATE() ) - CAST(azonetimes.day_of_week AS SIGNED INT) +
IF( WEEKDAY( CURDATE() ) > CAST(azonetimes.day_of_week AS SIGNED INT), 0, 7 ) - 7 DAY )) + `start`*60) )
AND (FROM_UNIXTIME(UNIX_TIMESTAMP(DATE(CURDATE() - INTERVAL WEEKDAY( CURDATE() ) - CAST(azonetimes.day_of_week AS SIGNED INT) +
IF( WEEKDAY( CURDATE() ) > CAST(azonetimes.day_of_week AS SIGNED INT), 0, 7 ) - 7 DAY )) + ((`range` + `start`)*60)))
)
OR
(
NOW()
#Check for this week
BETWEEN
(FROM_UNIXTIME(UNIX_TIMESTAMP(DATE(CURDATE() - INTERVAL WEEKDAY( CURDATE() ) - CAST(azonetimes.day_of_week AS SIGNED INT) +
IF( WEEKDAY( CURDATE() ) > CAST(azonetimes.day_of_week AS SIGNED INT), 0, 7 ) DAY )) + `start`*60) )
AND (FROM_UNIXTIME(UNIX_TIMESTAMP(DATE(CURDATE() - INTERVAL WEEKDAY( CURDATE() ) - CAST(azonetimes.day_of_week AS SIGNED INT) +
IF( WEEKDAY( CURDATE() ) > CAST(azonetimes.day_of_week AS SIGNED INT), 0, 7 ) DAY )) + ((`range` + `start`)*60)))
)
AS open
FROM azonetimes
+----+---------+-------------+-------+-------+------+
| id | zone_id | day_of_week | start | range | open |
+----+---------+-------------+-------+-------+------+
| 1 | 1 | 2 | 300 | 900 | 1 | << Wed 5am to 8pm
| 2 | 0 | 1 | 0 | 2400 | 1 | << Tue 00:00 to Wed 4pm (40 hrs)
| 3 | 4 | 4 | 300 | 900 | 0 | << Fri 5am to 8pm
| 4 | 0 | 5 | 900 | 7200 | 1 | << Sat 3pm to Thu 3pm
+----+---------+-------------+-------+-------+------+