我正在尝试对商店的关闭时间进行查询搜索。
问题是,由于某些场所在凌晨 2、3、4 点关闭 - 我如何查询where LESS THAN or EQUAL
04:00 的表行值何时对我来说比 23:00 更重要?
谢谢!
我正在尝试对商店的关闭时间进行查询搜索。
问题是,由于某些场所在凌晨 2、3、4 点关闭 - 我如何查询where LESS THAN or EQUAL
04:00 的表行值何时对我来说比 23:00 更重要?
谢谢!
我认为您应该使用日期时间、时间戳或时间作为数据类型。因为它将在未来用于数学计算。
如果您将其存储为字符串或数字,您将很难对其进行操作。
所以在选择数据类型时要具体。
选择一个任意日期来存储打开和关闭时间,比如 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
现在您可以使用大于和小于查询并获得您期望的结果。
由于收盘时间必须在开盘时间之后发生,因此如果closing_time
小于opening_time
该时间意味着间隔将在第二天滚动。
如何检查给定时间是否在间隔内?如果满足以下条件,则给定时间在区间内:
given_time
介于opening_time
和之间closing_time
,当区间不翻转时;given_time
不在 和 之间opening_time
,closing_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)
您必须检查是否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 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 );
该解决方案基于与上面发布的相同方法,但检查每个工作日的开放时间。