【问题标题】:How to find next (n) open days from SQL opening Hours Database如何从 SQL 开放时间数据库中查找下一个 (n) 开放日
【发布时间】:2015-04-23 01:46:57
【问题描述】:

我使用的 SQL 数据库架构类似于此链接上的架构。 Best way to store working hours and query it efficiently

我正在使用此基本架构存储某个位置的营业时间

  • 商店 - INTEGER
  • DayOfWeek - 整数 (0-6)
  • 开放时间 - 时间
  • 关闭时间 - 时间

但是,我要做的是为当前的 DateTime(即今天)获取商店营业的 NEXT (n) 天数。因此,例如,如果我不想找到商店在营业时间内营业和配置的接下来三天,则商店在周日关闭,今天的日期是 21/02/2015(星期六)我想要返回(21/02/2015)星期六、(23/02/2015)星期一和(23/02/2015)星期二

如果是星期日,我会返回(23/02/2015)星期一、(24/02/2015)星期二和(25/02/2015)星期三 (因为它在周日关闭)最后如果是 (20/02/2015)Friday 它会返回 (20/02/2015)Friday, (21/02/2015)Saturday, (23/02/2015 )星期一

我不知道这在 SQL 或 C# 中是否更容易做到,但如果弄清楚如何计算它,我会在精神上苦苦挣扎。

任何指针,指导都会很棒。

谢谢

【问题讨论】:

  • 就个人而言,我会使用 C# 而不是 SQL。一旦你想出了可行的解决方案,你总是可以尝试在 SQL 中重新实现算法。
  • 为了证明我之前的评论是正确的——您面临的数据读取非常少(您只需要给定商店营业的DayOfWeeks,最多 7 条记录),并且计算量适中。
  • 我同意@Grx70。从数据库中提取数据并在代码中执行您的逻辑。
  • 我从代码中提取哪些数据?在 c# 中获取接下来 20 天的日期,然后获取一组不同的开放日值,然后对每一行进行比较以删除与 dayofweek 整数不匹配的日期会更容易吗?这是我能理解的唯一方法。

标签: c# sql-server date


【解决方案1】:

试试这个:

DECLARE @t TABLE(WeekID INT, OpenTime time)
DECLARE @c INT = 10

INSERT INTO @t VALUES
(1, '10:00'),--sunday
(2, '10:00'),--monday
(4, '10:00'),--wednsday
(5, '10:00')--thursday


;WITH Tally (n) AS
(
    -- 1000 rows
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
SELECT TOP (@c) DATEADD(dd, t.n, GETDATE())
FROM Tally t
JOIN @t s ON DATEPART(w, DATEADD(dd, t.n, GETDATE())) = s.WeekID

输出:

Date
2015-02-22 --sunday
2015-02-23 --monday
2015-02-25 --wednsday
2015-02-26 --thursday
2015-03-01 --sunday
2015-03-02 --monday
2015-03-04 --wednsday
2015-03-05 --thursday
2015-03-08 --sunday
2015-03-09 --monday

PS:您可以将GETDATE() 替换为任何要查看的日期。

【讨论】:

  • 你不能假设这家店除了星期天每天都开门。问题是开放日期存储在表格中。
  • 可能是。好像没看懂这个问题
  • 对不起..出去了一个小时,没想到会有这么多结果..Andomar 是正确的,数据存储在表格中,所以基本上有可能关闭商店一个星期三和一个星期五,所以如果是星期二,我需要得到星期二、星期四和星期六。它并不总是一天关闭,也不总是只有三天......
  • @user257503,我怎么知道商店是关门还是开门?您能否提供该设置表中的示例数据?如果商店周一关门,周一没有记录,或者在 OpenTime 中为空?
【解决方案2】:

您可以使用case 使本周早些时候的一天看起来像下周的那一天。以下是查找下一个开放日的示例:

select  top 1 dateadd(day, day_diff, @dt) as dt
from    (
        select  case 
                when dayofweek <= datepart(dw, @dt) then dayofweek + 7
                else dayofweek
                end - datepart(dw, @dt) as day_diff
        ,       *
        from    dbo.OpeningHours
        ) sub1
order by
        day_diff

然后您可以递归查找超过一天。如果我们将上面的 sn-p 存储在一个名为 get_next_open_day 的函数中,递归公用表表达式可能如下所示:

; with  cte as
        (
        select  dbo.get_next_open_day(@dt) as open_day
        ,       1 as day_number
        union all
        select  dbo.get_next_open_day(prev_day.open_day)
        ,       prev_day.day_number + 1
        from    cte as prev_day
        where   prev_day.day_number < @number_of_days
        )
select  cte.open_day
,       datename(dw, cte.open_day)
from    cte
option  (maxrecursion 100)
;

这是一个完整的工作示例:

use Test

if object_id('OpeningHours') is not null
    drop table OpeningHours;
if object_id('dbo.get_next_open_day') is not null
    drop function dbo.get_next_open_day;

create table OpeningHours (dayofweek int, opentime time, closetime time);
insert dbo.OpeningHours values 
    (2, '9:00', '17:00'),
    (3, '9:00', '17:00'),
    (4, '9:00', '17:00'),
    (5, '9:00', '17:00'),
    (6, '9:00', '21:00'),
    (7, '10:00', '17:00')
    ;
go
create function dbo.get_next_open_day(
    @dt date) 
    returns date
as begin return
    (
    select  top 1 dateadd(day, day_diff, @dt) as dt
    from    (
            select  case 
                    when dayofweek <= datepart(dw, @dt) then dayofweek + 7
                    else dayofweek
                    end - datepart(dw, @dt) as day_diff
            ,       *
            from    dbo.OpeningHours
            ) sub1
    order by
            day_diff
    )
end
go

--declare @dt date = '2015-02-18' -- Wed
--declare @dt date = '2015-02-20' -- Fri
declare @dt date = '2015-02-22' -- Sun
declare @number_of_days int = 10

; with  cte as
        (
        select  dbo.get_next_open_day(@dt) as open_day
        ,       1 as day_number
        union all
        select  dbo.get_next_open_day(prev_day.open_day)
        ,       prev_day.day_number + 1
        from    cte as prev_day
        where   prev_day.day_number < @number_of_days
        )
select  cte.open_day
,       datename(dw, cte.open_day)
from    cte
option  (maxrecursion 100)
;

多个商店的实现留给读者作为练习。

【讨论】:

  • 我觉得我读到这里的时候脑袋都要爆炸了。. :) 我得好好想想这是怎么回事。
  • @user257503 这正是我强烈建议使用 C# 的原因 - 请参阅 juharr 的回答并想象在一年后必须审查代码(在这两种情况下)......
【解决方案3】:

首先,您可以使用如下所示的简单查询来获取商店营业的星期几

Select DayOfWeek
From OpenHours
Where ShopId = @ShopID

这假设在未开放的日子里不会有条目。如果打开时间列为空,或者小于或等于未打开日期的关闭时间,请调整此查询。

在运行该查询并取回结果并最好将其转换为 List&lt;DayOfWeek&gt; 后,您可以在代码中执行以下操作。

List<Day0fWeek> openDays = GetOpenDaysFromDB();
DateTime start = DateToStartFrom;
int n = numberOfDays;

List<DateTime> nextNOpenDays = new List<DateTime>();

while(nextNOpenDays.Count < n)
{
    if(openDays.Contains(start.DayOfWeek))
        nextNOpenDays.Add(start);
    start = start.AddDays(1);
}

【讨论】:

    【解决方案4】:

    这将以相当有效的方式为您提供最多 10 天的时间。第一次测试数据:

    DECLARE @DaysAhead TABLE (
        Delta INT
      )
    INSERT INTO @DaysAhead (Delta)
    SELECT 0
    UNION ALL SELECT 1
    UNION ALL SELECT 2
    UNION ALL SELECT 3
    UNION ALL SELECT 4
    UNION ALL SELECT 5
    UNION ALL SELECT 6
    UNION ALL SELECT 7
    UNION ALL SELECT 8
    UNION ALL SELECT 9
    UNION ALL SELECT 10
    
    DECLARE @Opening TABLE (
        Shop INT,
        DayOfWk INT,
        DayNm varchar(10),
        OpenTime TIME,
        CloseTime TIME
      )
    
    INSERT INTO @Opening (Shop, DayOfWk, DayNm, OpenTime, CloseTime)
    SELECT 1, 5, 'Fri', '09:00', '17:00' -- 
    UNION ALL SELECT 1, 6, 'Sat' ,'09:00', '17:00'
    --UNION ALL SELECT 0, 'Sun', '09:00', '17:00' -- Not open on Sunday
    UNION ALL SELECT 1, 1, 'Mon', '09:00', '17:00'
    UNION ALL SELECT 1, 2, 'Tue', '09:00', '17:00'
    UNION ALL SELECT 1, 3, 'Wed', '09:00', '17:00'
    

    可以这样查询:

    DECLARE @dt datetime='21-Feb-2015'
    DECLARE @dow int=datepart(dw, @dt)-1
    
    SELECT TOP 3 o.Shop, o.DayOfWk, o.DayNm, o.OpenTime, o.CloseTime FROM (
      SELECT Delta, ((@dow+Delta)%7) as DayOfWk 
      FROM @DaysAhead
    ) daysAhead
    INNER JOIN @Opening o on o.DayOfWk=daysAhead.DayOfWk
    ORDER BY daysAhead.Delta
    

    结果:

    DECLARE @dt datetime='20-Feb-2015' -- Fri
    
    1   5   Fri 09:00:00.0000000    17:00:00.0000000
    1   6   Sat 09:00:00.0000000    17:00:00.0000000
    1   1   Mon 09:00:00.0000000    17:00:00.0000000
    
    DECLARE @dt datetime='21-Feb-2015' -- Sat
    
    1   6   Sat 09:00:00.0000000    17:00:00.0000000
    1   1   Mon 09:00:00.0000000    17:00:00.0000000
    1   2   Tue 09:00:00.0000000    17:00:00.0000000
    
    DECLARE @dt datetime='22-Feb-2015' -- Sun
    
    1   1   Mon 09:00:00.0000000    17:00:00.0000000
    1   2   Tue 09:00:00.0000000    17:00:00.0000000
    1   3   Wed 09:00:00.0000000    17:00:00.0000000
    

    【讨论】:

    • 这看起来很有希望......让我看看我是否可以将它修补到我的数据库模型中。
    【解决方案5】:

    我设法找到了解决方案:

            public List<DateTime> getDaysOpen(int numberOfDays, DateTime start)
        {
            List<byte> openDays = this.getOpeningHoursDays();
            List<DateTime> nextNOpenDays = new List<DateTime>();
    
            while (nextNOpenDays.Count < numberOfDays)
            {
                if (openDays.Contains(Convert.ToByte(start.DayOfWeek)))
                    nextNOpenDays.Add(start);
                start = start.AddDays(1);
            }
            return nextNOpenDays;
        }
    
        public List<byte> getOpeningHoursDays()
        {
            return db.OpeningHours.Where(oh => oh.LocationId == this.Id).Select(oh => oh.DateOfWeek).ToList();
        }
    

    在我看来,这是找到解决方案的最简单方法。感谢您的所有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-16
      相关资源
      最近更新 更多