0

我有一张如下表:

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 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
4

2 回答 2

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

IE

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))

(如果时间在 start 之后和 end 之前,或者如果 start > end 并且时间在 start 之后或 end 之前,则区域处于活动状态)

于 2012-10-31T07:40:19.753 回答
0

废弃这个,看看 Terje D 的答案。

使用 Skpd 的想法的一种可能的方法(导致查询混乱):

我创建了一个开始日期(周一到周日的小整数 0 到 6),然后是一个开始时间(从午夜开始的整数分钟)和一个范围(整数分钟)。因此,区域处于活动状态的时间段介于 (day_of_week+start) 和 (day_of_week+start + range) 之间。我很确定有一种更优雅的方法,但我认为它适用于所有组合。并将存储量降至最低

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)

    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
+----+---------+-------------+-------+-------+------+
于 2012-10-31T07:59:39.270 回答