2

我正在尝试对商店的关闭时间进行查询搜索。

问题是,由于某些场所在凌晨 2、3、4 点关闭 - 我如何查询where LESS THAN or EQUAL04:00 的表行值何时对我来说比 23:00 更重要?

谢谢!

4

4 回答 4

2

我认为您应该使用日期时间、时间戳或时间作为数据类型。因为它将在未来用于数学计算。

如果您将其存储为字符串或数字,您将很难对其进行操作。

所以在选择数据类型时要具体。

于 2013-01-07T14:57:10.770 回答
2

选择一个任意日期来存储打开和关闭时间,比如 1900 年 1 月 1 日。

任何在同一天开门和关门的商店都使用这个日期。

因此,一家早上 9 点开门,下午 5 点关门的商店:

开放时间:1900-01-01 09:00:00

关闭时间:1900-01-01 17:00:00

一天开门,第二天关门的商店使用第二天作为关门时间。

因此,在下午 4 点开放并在第二天凌晨 2 点关闭的酒吧得到:

开放时间:1900-01-01 16:00:00

关闭:1900-01-02 02:00:00

现在您可以使用大于和小于查询并获得您期望的结果。

于 2013-01-10T14:48:54.417 回答
2

由于收盘时间必须在开盘时间之后发生,因此如果closing_time小于opening_time该时间意味着间隔将在第二天滚动。

如何检查给定时间是否在间隔内?如果满足以下条件,则给定时间在区间内:

  • given_time介于opening_time和之间closing_time,当区间不翻转时;
  • given_time不在 和 之间opening_timeclosing_time当关闭时间是第二天。

据我记得,我认为我以前从未在 SQL 查询中使用过XOR,但在这里它非常适合:

closing_time<opening_time
XOR
  (time(now()) >= least(opening_time,closing_time) and
   time(now()) < greatest(opening_time, closing_time))

如果在任何间隔内,这将给出 true time(now()),即使 close_time 小于 opening_time。请注意,我必须使用>=and <,使用>=and <=(或其等效的 BETWEEN)并不总是给出正确的结果,但我相信逻辑是正确的。

如果我们必须检查 time(now()) 是否在特定 dat 的间隔内,我们仍然会遇到一个问题:如果商店在星期一开门,凌晨 1 点关门,那么关门时间就不再是星期一了,所以如果我们过了midnignt,并且间隔被交换,我们必须检查前一天的间隔。这将检查正确的 opening_day:

opening_day =
date_format(now() - INTERVAL
    (closing_time<opening_time and time(now())<closing_time) DAY, '%a'))

如果关闭时间在第二天滚动,并且当前时间在关闭时间之前,则 () 中的条件将为真,因此我们必须检查 INTERVAL 1 DAY,否则条件为假并减去 INTERVAL 0 DAY 得出当天。

所以我的最终查询是这样的:

set @n='2013-01-11 18:15:00';

SELECT
  e.name,
  hrs.opening_day,
  hrs.opening_time,
  hrs.closing_time
FROM
  hours_of_operation hrs join establishments e
  on hrs.establishment_id = e.id
     and not is_closed
     and ((hrs.opening_day=date_format(@n -
           INTERVAL
             (closing_time<opening_time and time(@n)<closing_time)
           DAY, '%a'))
          AND (closing_time<opening_time XOR
         (time(@n) >= least(opening_time,closing_time)
          and time(@n) < greatest(opening_time, closing_time))))
 ;

这使所有商店在给定的@n 日期时间开放。小提琴在这里

编辑:

我有另一个(也许很奇怪?)想法来解决同样的问题。它应该更容易理解。2001 年的第一天'2001-01-01'是一个看起来不错的日期,并且还具有星期一的有趣特性。所以我的想法是将所有日期(当前日期、开幕日期、结束日期)移回这个日期。

当前日期将变为:

'2001-01-01'
+ INTERVAL WEEKDAY( @n ) DAY
+ INTERVAL TIME_TO_SEC( @n ) SECOND

(我们添加星期几,星期一的工作日为 0,因此我们仍将在“2001-01-01”,星期二为 1,因此将是“2001-01-02”,依此类推)。并使用 TIME_TO_SEC 我添加第二个以创建日期时间字段。

开幕日期为:

'2001-01-01'
+ INTERVAL
    FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1
  DAY
+ INTERVAL TIME_TO_SEC(opening_time) SECOND

将opening_day转换为要添加的天数我正在使用FIELD函数,然后我们必须从星期一开始减去1到1,然后我们添加时间部分。

截止日期为:

'2001-01-01'
 + INTERVAL
     FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
     -(closing_time>opening_time) DAY
 + INTERVAL TIME_TO_SEC(closing_time) SECOND

与开盘日期相同,但我将 1 减去 DAY 仅开盘时间在收盘时间之前,否则意味着收盘时间发生在第二天。然后我们只需要使用 BETWEEN 来检查我们是否在区间内,如下所示:

SELECT
  establishments.id,
  establishments.name,
  opening_time,
  closing_time
FROM
  `hours_of_operation` inner join establishments
  on hours_of_operation.establishment_id=establishments.id
     and not is_closed
     and
       '2001-01-01'
       + INTERVAL WEEKDAY( @n ) DAY
       + INTERVAL TIME_TO_SEC( @n ) SECOND

       BETWEEN

       '2001-01-01'
       + INTERVAL
           FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1
         DAY
       + INTERVAL TIME_TO_SEC(opening_time) SECOND

       AND

      '2001-01-01'
      + INTERVAL
          FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
          -(closing_time>opening_time) DAY
      + INTERVAL TIME_TO_SEC(closing_time) SECOND

在这里拉小提琴

但是在这一点上,我们可以从一周开始转换为秒,我们有所有的元素可以做到这一点,我的最终查询是这样的:

SELECT
  establishments.id,
  establishments.name,
  opening_time,
  closing_time
FROM
  `hours_of_operation` inner join establishments
  on hours_of_operation.establishment_id=establishments.id
     and not is_closed
     and
       WEEKDAY( @n ) * 86400 + TIME_TO_SEC( @n )    

       BETWEEN

       (FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')-1)*86400+
        TIME_TO_SEC(opening_time)

       AND

       (FIELD(opening_day,'Mon','Tue','Wed','Thu','Fri','Sat','Sun')
        -(closing_time>opening_time))*86400
        + TIME_TO_SEC(closing_time)
于 2013-01-10T23:37:33.330 回答
1

您必须检查是否closing_time小于opening_time才能知道这是第二天。

要获得给定日期时间的有效开放时间,您还必须检查给定日期时间是否小于开放时间以创建正确的时间间隔。

要获取给定日期时间的开放时间,请使用

SET @MyDate = Now();

SELECT 
  title,
  CAST( 
    CONCAT( 
      CASE 
        WHEN TIME( @MyDate ) < opening THEN 
          DATE( @MyDate ) - INTERVAL 1 DAY 
        ELSE DATE( @MyDate ) 
      END, ' ', opening 
    ) AS DATETIME 
  ) open_from, 
  CASE 
    WHEN opening > closing THEN 
      CAST( 
        CONCAT( 
          CASE 
            WHEN TIME( @MyDate ) < opening THEN 
              DATE( @MyDate ) - INTERVAL 1 DAY 
            ELSE DATE( @MyDate ) 
          END + INTERVAL 1 DAY, ' ', closing 
        ) AS DATETIME 
      )
    ELSE 
      CAST( 
        CONCAT( 
          CASE 
            WHEN TIME( @MyDate ) < opening THEN 
              DATE( @MyDate ) - INTERVAL 1 DAY 
            ELSE DATE( @MyDate ) 
          END, ' ', closing 
        ) AS DATETIME 
      )
  END open_till
FROM 
  stores
ORDER BY
  open_from;

获取实际open/closed状态使用

SET @MyDate = Now();

SELECT 
  title, 
  CASE
    WHEN 
      @MyDate >= CAST( CONCAT( CASE 
                           WHEN TIME( @MyDate ) < opening THEN 
                             DATE( @MyDate ) - INTERVAL 1 DAY 
                           ELSE DATE( @MyDate ) 
                         END, ' ', opening ) AS DATETIME )
    AND 
      @MyDate < CASE 
                WHEN opening > closing 
                THEN CAST( CONCAT( CASE 
                               WHEN TIME( @MyDate ) < opening THEN 
                                 DATE( @MyDate ) - INTERVAL 1 DAY 
                               ELSE DATE( @MyDate ) 
                             END + INTERVAL 1 DAY, ' ', closing ) AS DATETIME )
                ELSE CAST( CONCAT( CASE 
                               WHEN TIME( @MyDate ) < opening THEN 
                                 DATE( @MyDate ) - INTERVAL 1 DAY 
                               ELSE DATE( @MyDate ) 
                             END, ' ', closing ) AS DATETIME )
              END
    THEN 'open'
    ELSE 'closed'
  END state
FROM 
  stores;

SQL 小提琴演示

更新

我已经处理了您的SQL Fiddle Sample并删除了过时的列closing_day

SET @MyTime = Now();

SELECT
  ho.`establishment_id`, e.`name`
FROM
  `hours_of_operation` ho
JOIN
  `establishments` e ON ho.`establishment_id` = e.`id`
WHERE
  NOT `is_closed`
AND
  `opening_day` = DATE_FORMAT( CASE 
                                 WHEN TIME( @MyTime ) < `opening_time` THEN 
                                   DATE( @MyTime ) - INTERVAL 1 DAY 
                                 ELSE 
                                   DATE( @MyTime ) 
                               END, '%a' )
AND
  @MyTime >= CAST( CONCAT( CASE 
                             WHEN TIME(@MyTime) < ho.`opening_time` THEN 
                               DATE( @MyTime ) - INTERVAL 1 DAY 
                             ELSE 
                               DATE( @MyTime ) 
                           END, ' ', ho.`opening_time` ) AS DATETIME )
AND
  @MyTime < CAST( CASE
                    WHEN ho.`opening_time` > ho.`closing_time` THEN
                      CONCAT( CASE 
                                WHEN TIME(@MyTime) < ho.`opening_time` THEN 
                                  DATE( @MyTime ) - INTERVAL 1 DAY 
                                ELSE 
                                  DATE( @MyTime ) 
                              END + INTERVAL 1 DAY, ' ', ho.`closing_time` )
                    ELSE
                      CONCAT( CASE 
                                WHEN TIME(@MyTime) < ho.`opening_time` THEN
                                  DATE( @MyTime ) - INTERVAL 1 DAY 
                                ELSE 
                                  DATE( @MyTime ) 
                              END, ' ', ho.`closing_time` )
                  END AS DATETIME );

该解决方案基于与上面发布的相同方法,但检查每个工作日的开放时间。

SQL 小提琴演示

于 2013-01-10T09:48:40.337 回答