T-SQL :根据现有 table 和输入参数开发日期范围或日期序列,不使用 DB 对象

T-SQL : develop date ranges or date sequences from an existing table and input parameters, WITHOUT using DB objects

我在 SQL Server 2016 中有一个场景,使用 T-SQL 语言。

我有一个来源table,如下:

虽然 ABC 和 HDF 的日期范围在月份(4 月)内不完整,但 PQR 的范围是完整的月份(完全完整),而 RST 的范围是完整的,有 2 个序列(没有任何中断)月份)。

我要为此 table 传递两个参数,都是日期值(通常是一个月的开始和结束)。

DECLARE @RangeStart AS date = '2021-04-01'
DECLARE @RangeEnd AS date = '2021-04-30'

我需要将上面的源 table 更改为如下所示的两个输出 table,涵盖从 4 月 1 日到 4 月 30 日的整个日期范围:

有人可以为我提供一个 T-SQL 查询来使用源 table 开发两个输出 tables 吗?

我的报表应用程序有一个限制,我不能使用任何数据库对象,例如临时 tables (#)、table 变量 ( @)、CTE (WITH) 等

我需要一个纯粹的临时查询,只开发派生的 tables,如下所示:

示例:

SELECT * 
FROM
    (SELECT ...
     FROM tableX
     INNER JOIN tableY ON ...
     ) DerivedTable

我需要在 'DerivedTable' 中完成所有数据操作, 不使用任何数据库对象

如果您能为我提供开发两个 table 之一的解决方案,那就太好了。

从类似这样的东西开始,生成一个序列table,只要你需要。

DECLARE @RangeStart AS date = '2021-04-01'
DECLARE @RangeEnd AS date = '2021-04-30'


select dateadd(day,q.i, @RangeStart)
from (select row_number() over (order by (select null))-1 i 
      from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) v1(i),
           (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) v2(i)
      ) q
where q.i <= datediff(day,@rangeStart,@RangeEnd)

这是第二个结果集的答案。

与@David Browne 类似的方法 - Microsoft,使用不同的方法来“制作”日期 table

Create the sample data, shown in the question

create table src (
     id char(3)
    ,name char(3)
    ,value int
    ,startDate date
    ,endDate date
)
go
insert into src values 
('abc','xyz',250,'20210410','20210415')
,('abc','xyz',150,'20210424','20210430')

Description: I created 2 derived tables a, b which have matching dates to the "date" table, and all dates, respectively. This was needed in order to get all the dates to show up in the final result set. Getting the "value" from the matching table allows for you to get the values for those dates in which we have a value. The 4th field, date, can really be obtained from either derived table

Query:

DECLARE @RangeStart AS date = '2021-04-01';
DECLARE @RangeEnd AS date = '2021-04-30';

select 
     b.id
    ,b.name
    ,a.value
    ,a.date
from 
(
SELECT
        id
        ,name
        ,value
        ,DateTable.Date
    FROM 
    (
    SELECT  TOP (DATEDIFF(DAY, @RangeStart, @RangeEnd) + 1)
            Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @RangeStart)
    FROM    sys.objects a
    )DateTable 
    left outer join src
    ON DateTable.Date between src.startdate and src.enddate
) a

inner join (
    SELECT TOP (DATEDIFF(DAY, @RangeStart, @RangeEnd) + 1)
        id
        ,name
        ,NULL as value 
        ,DateTable.Date
    FROM 
    (
    SELECT  TOP (DATEDIFF(DAY, @RangeStart, @RangeEnd) + 1)
            Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @RangeStart)
    FROM    sys.objects a
    )DateTable 
    inner join src ON 1=1
    )b on a.date = b.date 

2021 年 8 月 11 日更新:已修改以说明多个 ID 改变了什么:

  • 第二个嵌套查询(返回所有没有值的日期)已修改为具有 DISTINCT 而不是 TOP,因为旧方法会将其限制为 30 条记录。没有 TOP 你会有一个笛卡尔积,因此 DISTINCT 子句每个 day/id.
  • 带回一条记录
  • 两个派生 table 之间的连接从 INNER 更改为 RIGHT 连接,因为内部只会带来 没有的日期记录在左边table(只有一个,即使多个ID都需要),虽然我们想把它们都带回来
  • 最外层的查询改为让 DATE 字段来自“右边”table。
  • 两个派生 table 之间的连接现在连接日期和 ID,否则我们会得到笛卡尔积

查询:

DECLARE @RangeStart AS date = '2021-04-01';
DECLARE @RangeEnd AS date = '2021-04-30';

SELECT 
     b.id
    ,b.name
    ,a.value
    ,b.[date]
FROM 
(
SELECT
         id
        ,name
        ,value
        ,DateTable.[Date]
    FROM 
    (
    SELECT  TOP (DATEDIFF(DAY, @RangeStart, @RangeEnd) + 1)
            Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @RangeStart)
    FROM    sys.objects a
    )DateTable 
    left outer join src
    ON DateTable.Date between src.startdate and src.enddate
) a

RIGHT OUTER JOIN (
    SELECT DISTINCT 
         id
        ,name
        ,NULL as value 
        ,DateTable.Date
    FROM 
    (
    SELECT  TOP (DATEDIFF(DAY, @RangeStart, @RangeEnd) + 1)
            Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @RangeStart)
    FROM    sys.objects a
    )DateTable 
    INNER JOIN src ON 1=1
    )
    b ON a.[date] = b.[date] 
    AND a.id = b.id
ORDER BY 1,4;

你可以试试这样的

drop table if exists #src;
go
create table #src(
     id char(3)
    ,name char(3)
    ,value int
    ,startDate date
    ,endDate date);

insert into #src values 
('abc','xyz',250,'20210410','20210415')
,('abc','xyz',150,'20210424','20210430');

declare 
  @RangeStart AS date = '2021-04-01',
  @RangeEnd AS date = '2021-04-30';

with
l as (select 1 n from (values (1),(1),(1),(1),(1),(1),(1),(1)) as v(n)),
range_cte as (
    select top (datediff(day, @RangeStart, @RangeEnd)+1) 
           dateadd(day, row_number() over (order by (select null))-1, @RangeStart) dt
    from l l1, l l2,l l3, l l4) /* 8^4 sequence n */ 
select s.id, s.name, s.startDate, s.endDate, r.dt,
       case when r.dt>=s.startDate and r.dt<=s.endDate then s.value
            else null end calc_val
from range_cte r
     cross join #src s 
order by s.id, s.name, s.startDate, s.endDate, r.dt;

[编辑] 没有 CTE

declare 
  @RangeStart AS date = '2021-04-01',
  @RangeEnd AS date = '2021-04-30';

select s.id, s.name, s.startDate, s.endDate, r.dt,
       case when r.dt>=s.startDate and r.dt<=s.endDate then s.value
            else null end calc_val
from (
    select top (datediff(day, @RangeStart, @RangeEnd)+1) 
           dateadd(day, row_number() over (order by (select null))-1, @RangeStart) dt
    from (values (1),(1),(1),(1),(1),(1),(1),(1)) l1(n),
         (values (1),(1),(1),(1),(1),(1),(1),(1)) l2(n),
         (values (1),(1),(1),(1),(1),(1),(1),(1)) l3(n),
         (values (1),(1),(1),(1),(1),(1),(1),(1)) l4(n)) r
     cross join #src s 
order by s.id, s.name, s.startDate, s.endDate, r.dt;