我在此表中存储营业时间。企业可以在同一天拥有多个营业时间。 结束时间可以在当天午夜之后。
CREATE TABLE [Process].[OpeningHours](
[openinghoursid] [int] IDENTITY(1,1) NOT NULL,
[businessid] [int] NOT NULL,
[daynumber] [int] NOT NULL,
[opentime] [time](7) NOT NULL,
[duration] [int] NOT NULL,
[closetime] AS (dateadd(minute,[duration],[opentime])
)
此表中的示例数据包括:
INSERT [Process].[OpeningHours]
([openinghoursid], [businessid], [daynumber], [opentime], [duration])
VALUES (79, 18, 2, CAST(N'12:00:00' AS Time), 165),
(80, 18, 2, CAST(N'18:00:00' AS Time), 240),
(81, 18, 3, CAST(N'12:00:00' AS Time), 165),
(82, 18, 3, CAST(N'18:00:00' AS Time), 240),
(83, 18, 4, CAST(N'12:00:00' AS Time), 165),
(84, 18, 4, CAST(N'18:00:00' AS Time), 240),
(85, 18, 5, CAST(N'12:00:00' AS Time), 165),
(86, 18, 5, CAST(N'18:00:00' AS Time), 240),
(87, 18, 6, CAST(N'12:00:00' AS Time), 165),
(88, 18, 6, CAST(N'18:00:00' AS Time), 300),
(89, 18, 7, CAST(N'12:00:00' AS Time), 165),
(90, 18, 7, CAST(N'18:00:00' AS Time), 600),
(91, 18, 1, CAST(N'12:00:00' AS Time), 180);
现在我想创建一个在业务当前处于打开或关闭状态时返回的函数。
CREATE FUNCTION [Process].[ufnIsSpaceOpen](@businessid int)
RETURNS BIT
AS
BEGIN
DECLARE @currentdatetime DATETIME = GETDATE();
DECLARE @dayofweek INT = DATEPART(dw,@currentdatetime);
DECLARE @currentdate DATETIME = CONVERT(DATE, @currentdatetime);
DECLARE @isopen BIT;
SELECT @isopen = COUNT(*)
FROM Process.OpeningHours
WHERE
daynumber = @dayofweek
AND businessid = @businessid
AND
(
@currentdatetime >= @currentdate + CONVERT(DATETIME, opentime)
AND
@currentdatetime <=
CASE
WHEN closetime < '00:00:00' THEN @currentdate + CONVERT(DATETIME, closetime)
ELSE DATEADD(DAY,1,@currentdate) + CONVERT(DATETIME, closetime)
END
);
RETURN @isopen;
END;
GO
我正在使用COUNT()来查看是否有任何行匹配条件,如果0匹配则意味着它已关闭,如果COUNT()大于0则它是打开的。这在关闭时间在同一天内时有效,但是当关闭时间在午夜之后或当前时间在午夜之后时它不起作用。
知道如何解决它吗?
编辑: 感谢您的所有回复。最后,我继续使用@ DenisRubashkin的解决方案。对于任何感兴趣的人,这是我使用的最终功能:
CREATE FUNCTION [Process].[ufnIsSpaceOpen](@businessid int)
RETURNS BIT
AS
BEGIN
DECLARE @isopen BIT;
DECLARE @Date DATETIME = GETDATE();
SELECT @isopen = COUNT(*)
FROM
(
SELECT (CAST(CAST(@Date AS DATE) AS DATETIME) + CAST(h.opentime AS DATETIME)) AS Opened,
DATEADD(mi, h.duration, (CAST(CAST(@Date AS DATE) AS DATETIME) + CAST(h.opentime AS DATETIME))) AS Closed
FROM Process.OpeningHours h
WHERE h.daynumber = DATEPART(dw, @Date)
AND businessid = @businessid
UNION
SELECT (CAST(DATEADD(day, -1, CAST(@Date AS DATE)) AS DATETIME) + CAST(h.opentime AS DATETIME)) AS Opened,
DATEADD(mi, h.duration, (CAST(DATEADD(day, -1, CAST(@Date AS DATE)) AS DATETIME) + CAST(h.opentime AS DATETIME))) AS Closed
FROM Process.OpeningHours h
WHERE h.daynumber = CASE WHEN DATEPART(dw, @Date) = 1
THEN 7
ELSE DATEPART(dw, @Date) - 1
END
AND businessid = @businessid
) w
WHERE @Date BETWEEN Opened AND Closed
RETURN @isopen;
END;
答案 0 :(得分:3)
SQL Server的日期/时间函数非常糟糕。这是一个获得今天工作日开放时间的查询,并将其转换为今天的开放时间(例如周四13:00至2017-03-30 13:00)。然后用它来检查它现在是否打开。
select count(*) as isopen
from
(
select
cast(cast(getdate() as date) as datetime) + opentime as opendatetime,
duration
from process.openinghours
where daynumber = datepart(dw, getdate())
) as today
where getdate() between opendatetime and dateadd(mi, duration, opendatetime);
您可以简单地将此功能应用到您的功能中,以使其更具可读性和便捷性。
更新:我们也必须考虑昨天午夜开放时间。感谢Serg指出这一点。不幸的是,查询变得更加复杂:
select count(*) as isopen
from
(
select -- today's opening hours
cast(cast(getdate() as date) as datetime) + opentime as opendatetime,
duration
from process.openinghours
where daynumber = datepart(dw, getdate())
union all
select -- yesterday's opening hours
cast(dateadd('d', -1, cast(getdate() as date)) as datetime) + opentime as opendatetime,
duration
from process.openinghours
where daynumber % 7 + 1 = datepart(dw, getdate())
) as today_and yesterday
where getdate() between opendatetime and dateadd(mi, duration, opendatetime);
更新2:这是一个简化版本,我将所有开放时间转换为过去六天加上今天(希望我的数学正确)。我还在上面的查询中添加了我忘记的businessid。
select count(*) as isopen
from
(
select
cast(dateadd('d', - (7 + datepart(dw, getdate()) - daynumber) % 7, cast(getdate() as date)) as datetime) + opentime as opendatetime,
duration
from process.openinghours
where businessid = @businessid
) as these_last_seven_days
where getdate() between opendatetime and dateadd(mi, duration, opendatetime);
答案 1 :(得分:2)
这应该是你的伎俩
SELECT @isopen = COUNT(*)
FROM [Process].[OpeningHours]
WHERE daynumber = @dayofweek
AND businessid = @businessid
AND @CheckDateTime BETWEEN CAST(CONCAT('1900-01-01 ',(CAST([opentime] AS TIME))) AS DATETIME2) AND DATEADD(minute,[duration],CAST(CONCAT('1900-01-01 ',(CAST([opentime] AS TIME))) AS DATETIME2))
DECLARE @currentdatetime DATETIME = '2017-03-30 13:01:51.550';
DECLARE @dayofweek INT = DATEPART(dw, @currentdatetime);
DECLARE @currentTime VARCHAR(16) = CAST(@currentdatetime AS Time)
DECLARE @CheckDateTime DATETIME2 = CAST(CONCAT('1900-01-01 ',(CAST(@currentTime AS TIME))) AS DATETIME2)
DECLARE @isopen BIT;
DECLARE @businessid INT = 18
DECLARE @BusinessOpeningHours TABLE ([openinghoursid] [int] NOT NULL,[businessid] INT NOT NULL,
[daynumber] [int] NOT NULL,
[opentime] [time](7) NOT NULL,
[duration] [int] NOT NULL,
[closetime] [time] NOT NULL)
INSERT INTO @BusinessOpeningHours([openinghoursid], [businessid], [daynumber], [opentime], [duration],[closetime])
SELECT 79, 18, 2, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 80, 18, 2, CAST(N'18:00:00' AS Time), 240, dateadd(minute,240,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 81, 18, 3, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 82, 18, 3, CAST(N'18:00:00' AS Time), 240, dateadd(minute,240,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 83, 18, 4, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 84, 18, 4, CAST(N'18:00:00' AS Time), 240, dateadd(minute,240,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 85, 18, 5, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 86, 18, 5, CAST(N'18:00:00' AS Time), 240, dateadd(minute,240,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 87, 18, 6, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 88, 18, 6, CAST(N'18:00:00' AS Time), 300, dateadd(minute,300,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 89, 18, 7, CAST(N'12:00:00' AS Time), 165, dateadd(minute,165,CAST(N'12:00:00' AS Time)) UNION ALL
SELECT 90, 18, 7, CAST(N'18:00:00' AS Time), 600, dateadd(minute,600,CAST(N'18:00:00' AS Time)) UNION ALL
SELECT 91, 18, 1, CAST(N'12:00:00' AS Time), 180, dateadd(minute,180,CAST(N'12:00:00' AS Time))
SELECT @isopen = COUNT(*)
FROM @BusinessOpeningHours
WHERE daynumber = @dayofweek
AND businessid = @businessid
AND @CheckDateTime BETWEEN CAST(CONCAT('1900-01-01 ',(CAST([opentime] AS TIME))) AS DATETIME2) AND DATEADD(minute,[duration],CAST(CONCAT('1900-01-01 ',(CAST([opentime] AS TIME))) AS DATETIME2))
SELECT @isopen;
答案 2 :(得分:1)
将具有日间过境间隔的行转换为行对,并简单地检查之间的时间。演示
with [OpeningHours+] as (
select oh.[openinghoursid], oh.[businessid], oh.duration, t.*
from [OpeningHours] oh
cross apply (
select [daynumber], [opentime]
,case when datediff(minute,[opentime],'23:59:59') + 1 >= [duration]
then dateadd(minute,[duration],[opentime])
else cast('23:59:59' as time) end [closetime]
union all
select case [daynumber] when 7 then 1 else [daynumber] + 1 end, cast('00:00:00' as time)
, dateadd(minute,[duration] - (datediff(minute,[opentime],'23:59:59') + 1), cast('00:00:00' as time))
where datediff(minute,[opentime],'23:59:59') + 1 < [duration]
) t
)
select *
from [OpeningHours+]
where [businessid] = 18 and [daynumber] = 1 and cast('00:03:00' as time) between [opentime] and [closetime]
答案 3 :(得分:1)
CREATE TABLE #OpeningHours(
[openinghoursid] int,
[businessid] int NOT NULL,
[daynumber] int NOT NULL,
[opentime] time NOT NULL,
[duration] int NOT NULL,
)
INSERT #OpeningHours
VALUES (79, 18, 2, CAST(N'12:00:00' AS Time), 165),
(80, 18, 2, CAST(N'18:00:00' AS Time), 240),
(81, 18, 3, CAST(N'12:00:00' AS Time), 165),
(82, 18, 3, CAST(N'18:00:00' AS Time), 240),
(83, 18, 4, CAST(N'12:00:00' AS Time), 165),
(84, 18, 4, CAST(N'18:00:00' AS Time), 240),
(85, 18, 5, CAST(N'12:00:00' AS Time), 165),
(86, 18, 5, CAST(N'18:00:00' AS Time), 240),
(87, 18, 6, CAST(N'12:00:00' AS Time), 165),
(88, 18, 6, CAST(N'18:00:00' AS Time), 300),
(89, 18, 7, CAST(N'12:00:00' AS Time), 165),
(90, 18, 7, CAST(N'18:00:00' AS Time), 600),
(91, 18, 1, CAST(N'12:00:00' AS Time), 180);
DECLARE @Date DATETIME
SELECT @Date = GETDATE()
SELECT COUNT(*)
FROM
(
SELECT (CAST(CAST(@Date AS DATE) AS DATETIME) + h.opentime) AS Opened,
DATEADD(mi, h.duration, (CAST(CAST(@Date AS DATE) AS DATETIME) + h.opentime)) AS Closed
FROM #OpeningHours h
WHERE h.daynumber = DATEPART(dw, @Date)
UNION
SELECT (CAST(DATEADD(day, -1, CAST(@Date AS DATE)) AS DATETIME) + h.opentime) AS Opened,
DATEADD(mi, h.duration, (CAST(DATEADD(day, -1, CAST(@Date AS DATE)) AS DATETIME) + h.opentime)) AS Closed
FROM #OpeningHours h
WHERE h.daynumber = CASE WHEN DATEPART(dw, @Date) = 1
THEN 7
ELSE DATEPART(dw, @Date) - 1
END
) w
WHERE @Date BETWEEN Opened AND Closed
DROP TABLE #OpeningHours