如何使用日期范围计算只有 "day of week" 的值的总和?

How to calculate sum of a value with only "day of week" using a date-range?

我有2个table就是

  1. 项目 table 包含 item_id、store_id、offer_start_Date 和 offer_end_date
  2. 的详细信息
  3. 商店 table 有 store_id、day_of_week、store_hours

两个table的结构如下- 1) Item_Table :

Store ID Item ID offer_start_Date offer_end_date
NY0001 FMC0001 2021-10-30 2021-11-04
NY0001 FMC0002 2021-11-08 Null
NY0002 FMC0003 2021-11-02 2021-11-02
NY0002 FMC0004 2021-09-01 2021-10-10

2) 存储 Table :

Store ID Day of Week store Hours
NY0001 Monday 12
NY0001 Tuesday 12
NY0001 Wednesday 12
NY0001 Thursday 12
NY0001 Friday 0
NY0001 Saturday 14
NY0001 Sunday 0
NY0002 Monday 8
NY0002 Tuesday 8
NY0002 Wednesday 8
NY0002 Thursday 8
NY0002 Friday 8
NY0002 Saturday 8
NY0002 Sunday 8

需要确定在给定日期范围内该商品在商店中的可用时间。

假设我们需要找到 item_id 在“2021-11-01”和“2021-11-10”之间的总可用小时数。预期结果如下(当offer_end_date不可用时按今天计算)-

Store ID Item ID offer_start_Date offer_end_date Total_available_hours
NY0001 FMC0001 2021-10-30 2021-11-04 48
NY0001 FMC0002 2021-11-08 Null 36
NY0002 FMC0003 2021-11-02 2021-11-02 8
NY0002 FMC0004 2021-09-01 2021-10-10 0

解释:

  1. FMC0001 - 营业时间仅从“2021-11-01”到“2021-11-04”(4 整天)
  2. FMC0002 - 仅在“2021-11-07”和“2021-11-10”之间可用(周日至周三)
  3. FMC0003 - 一整天可用的时间
  4. FMC0004 - 超出查询日期范围

这里有一个方法可以做到这一点

create table item_table(store_id varchar(10),item_id varchar(50),offer_start_date date,offer_end_date date);

insert into item_table
select 'NY0001','FMC0001','2021-10-30','2021-11-04' union all
select 'NY0001','FMC0002','2021-11-08',Null union all
select 'NY0002','FMC0003','2021-11-02','2021-11-02' union all
select 'NY0002','FMC0004','2021-09-01','2021-10-10';

create table store_table(store_id varchar(10),day_of_week varchar(50),store_hours int);

insert into store_table
select 'NY0001','Monday',   12         union all
select 'NY0001','Tuesday',    12 union all
select 'NY0001','Wednesday',     12 union all
select 'NY0001','Thursday', 12 union all
select 'NY0001','Friday',    0 union all
select 'NY0001','Saturday', 14 union all
select 'NY0001','Sunday',    0 union all
select 'NY0002','Monday',    8 union all
select 'NY0002','Tuesday',     8 union all
select 'NY0002','Wednesday',      8 union all
select 'NY0002','Thursday',  8 union all
select 'NY0002','Friday',    8 union all
select 'NY0002','Saturday',  8 union all
select 'NY0002','Sunday',    8;

with data
  as (
select a.store_id
      ,a.item_id
      ,a.offer_start_date
      ,a.offer_end_date
      ,dateadd(day,m.rnk,a.offer_start_date) as days_involved     
      ,s.day_of_week
      ,s.store_hours
  from item_table a
cross apply (select * 
               from (select row_number() over(order by (select null))-1 as rnk
                       from master..spt_values
                     )x
              where x.rnk<=datediff(day,offer_start_date,isnull(offer_end_date,getdate()))
             )m
  join store_table s
    on a.store_id=s.store_id
   and s.day_of_week=datename(weekday,dateadd(day,m.rnk,a.offer_start_date))
       ) 
select store_id,item_id,offer_start_date,offer_end_date
      ,sum(case when days_involved between '2021-11-01' and '2021-11-10' then 
                     store_hours
                else 0 
            end) as total_work_hours
  from data
group by store_id,item_id,offer_start_date,offer_end_date  
order by store_id,item_id

+==========+=========+==================+================+==================+
| store_id | item_id | offer_start_date | offer_end_date | total_work_hours |
+==========+=========+==================+================+==================+
| NY0001   | FMC0001 | 2021-10-30       | 2021-11-04     | 48               |
+----------+---------+------------------+----------------+------------------+
| NY0001   | FMC0002 | 2021-11-08       | (null)         | 36               |
+----------+---------+------------------+----------------+------------------+
| NY0002   | FMC0003 | 2021-11-02       | 2021-11-02     | 8                |
+----------+---------+------------------+----------------+------------------+
| NY0002   | FMC0004 | 2021-09-01       | 2021-10-10     | 0                |
+----------+---------+------------------+----------------+------------------+

demo

https://sqlize.online/sql/mssql2017/7f6721a9dbc954661d0c2db44c089bc4/

您可以通过为输入日期范围内的所有日期创建临时 table 来实现。

示例数据创建脚本

CREATE TABLE item_table(store_id VARCHAR(20),item_id VARCHAR(20),offer_start_date DATE,offer_end_date DATE)

INSERT INTO item_table(store_id,item_id,offer_start_date,offer_end_date)
VALUES ('NY0001','FMC0001','2021-10-30','2021-11-04'),
('NY0001','FMC0002','2021-11-08',null),
('NY0002','FMC0003','2021-11-02','2021-11-02'),
('NY0002','FMC0004','2021-09-01','2021-10-10')

CREATE TABLE store_table(store_id VARCHAR(20),day_of_week VARCHAR(20),store_hour int)

INSERT INTO store_table(store_id,day_of_week,store_hour)
VALUES  ('NY0001','Monday',12),
('NY0001','Tuesday',12),
('NY0001','Wednesday',12),
('NY0001','Thursday',12),
('NY0001','Friday',0),
('NY0001','Saturday',14),
('NY0001','Sunday',0),
('NY0002','Monday',8),
('NY0002','Tuesday',8),
('NY0002','Wednesday',8),
('NY0002','Thursday',8),
('NY0002','Friday',8),
('NY0002','Saturday',8),
('NY0002','Sunday',8)

最终脚本将是,

-- Input date range
DECLARE @StartDate DATE, @EndDate DATE;
SELECT @StartDate = '2021-11-01', @EndDate = '2021-11-10'; 

-- Create and populate a temporary table
DECLARE @dateList TABLE (date DATE,day_name VARCHAR(20));

WITH DateList(date,day_name) AS (SELECT @StartDate AS DATE,DATENAME(dw,@StartDate) UNION ALL SELECT DATEADD(DAY,1,date), DATENAME(dw,DATEADD(DAY,1,date)) FROM DateList WHERE date < @EndDate)

INSERT INTO @dateList(date,day_name) SELECT * FROM DateList;


-- Main query
SELECT a.store_id,item_id,offer_start_date,offer_end_date,
CASE WHEN SUM(store_hour) IS NULL THEN 0 ELSE SUM(store_hour) END AS 'total_available_hours'
FROM
(
  SELECT store_id,item_id,offer_start_date,offer_end_date,date,day_name
  FROM item_table i
  LEFT JOIN @dateList ON((date BETWEEN offer_start_date AND offer_end_date) OR (offer_end_date IS NULL AND date>=offer_start_date AND date <= GETDATE()))
)a
LEFT JOIN 
(
  SELECT store_id,day_of_week,store_hour
  FROM store_table
)b ON (a.store_id = b.store_id AND a.day_name=b.day_of_week)
GROUP BY a.store_id,item_id,offer_start_date,offer_end_date;