向resultset添加动态行的T-SQL

向resultset添加动态行的T-SQL,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有两张桌子: 房间 合同租金合同 关系:合同n-1房间 我获取用户输入的开始日期和结束日期。根据这个时间间隔,我查询与房间相关的合同。相关手段: 在给定的时间间隔内租用一个房间 在给定的时间间隔内开始租用房间 在给定的时间间隔内,房间停止出租 我的问题是: SELECT Room.id, RentContract.activeon, RentContract.expireson FROM RentContract INNER JOIN Room ON RentCon

我有两张桌子:

房间 合同租金合同 关系:合同n-1房间 我获取用户输入的开始日期和结束日期。根据这个时间间隔,我查询与房间相关的合同。相关手段:

在给定的时间间隔内租用一个房间 在给定的时间间隔内开始租用房间 在给定的时间间隔内,房间停止出租 我的问题是:

SELECT Room.id,
       RentContract.activeon,
       RentContract.expireson
FROM RentContract
INNER JOIN Room ON RentContract.roomid = Room.id
WHERE (RentContract.new_activeon >= @startDate
       OR RentContract.new_activeon IS NULL)
  AND (RentContract.new_expireson <= @endDate
       OR RentContract.new_expireson IS NULL)
现在,要求我另外显示这些房间的非租用间隔。由于我的数据库中没有这些,我想我需要插入一些动态行,我将在同一个列表中显示这些行。此外,我将在结果状态中显示一个额外的列,该列将显示实际合同的已占用状态和动态行的空状态

例如,用户输入为:startDate=01.05.2016,endDate=01.07.2016

我现在的结果是:

我期望的结果是:

所以我实际上需要用db记录或动态记录填充整个输入间隔

@伦堡 你的解决方案几乎对我有效!还有一个小细节:

我得到一些结果,合同在一个月的第二天开始,所以我想我需要在那天有一个空条目。例如,2016年1月1日至2016年1月1日为空。以下是我的初始结果的一些子集,以及我从您那里得到的结果。在特定场景中,我将解决方案标记为黄色:

初始查询:

伦波尔的查询:


您的查询是否有一些小的调整来解决这个问题?

您可以在cte的帮助下完成。由于您尚未提供更多数据-将使用发布的内容:

DECLARE @startdate date = '2016-05-01',
        @enddate date = '2016-07-01'

;WITH cte AS (
    SELECT r.id,
           rc.activeon,
           rc.expireson,
           'Occupited' as [state]
    FROM RentContract rc
    INNER JOIN Room r
        ON rc.roomid = r.id
    WHERE (rc.new_activeon >= @startDate OR rc.new_activeon IS NULL)
      AND (rc.new_expireson <= @endDate OR rc.new_expireson IS NULL)
)


SELECT *
FROM cte
UNION ALL
SELECT  id, 
        activeon, 
        CASE WHEN expireson < activeon THEN @enddate ELSE expireson END as expireson,
        'Empty' as [state]
FROM (
    SELECT id, CAST(DATEADD(day,1,expireson) as date) as activeon, ROW_NUMBER() OVER (ORDER BY expireson ASC) as rn
    FROM cte
    ) as s
INNER JOIN (
    SELECT CAST(DATEADD(day,-1,activeon)as date) as expireson, ROW_NUMBER() OVER (ORDER BY expireson ASC) as rn
    FROM cte
    ) as e
    ON e.rn=s.rn+1
ORDER BY id, activeon

你需要几个CTE来解决这个问题。其基本思想是获得您已经拥有的所有占用时间,然后使用结果集中的日期来查找每个房间的间隔

首先是完整的查询:

declare @startDate smalldatetime = '20160501',
        @endDate smalldatetime = '20160701'

; with occupieds as (
    SELECT Room.id,
           RentContract.activeon,
           RentContract.expireson,
           'Occupied' as [State],
           -- get ordering of contract for each rooms
           row_number() over (partition by roomid order by activeon) SortOrder
    FROM RentContract
    INNER JOIN Room ON RentContract.roomid = Room.id
    WHERE (RentContract.activeon >= @startDate
           OR RentContract.activeon IS NULL)
      AND (RentContract.expireson <= @endDate
           OR RentContract.expireson IS NULL)
),
empties as (
    select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
        inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
),
extremes as (
    select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
        having min(activeon) > @startDate
    union all
    select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
        having max(expireson) < @enddate
)
select id, activeon, expireson, [State] from occupieds
    union all
select id, activeon, expireson, [State] from empties
    union all
select id, activeon, expireson, [State] from extremes
order by id, activeon
这就产生了以下结果

id  | activeon            | expireson           | State    | SortOrder
1   | 2016-05-01 00:00:00 | 2016-05-31 00:00:00 | Occupied | 1
1   | 2016-06-15 00:00:00 | 2016-06-25 00:00:00 | Occupied | 2
2   | 2016-05-01 00:00:00 | 2016-07-01 00:00:00 | Occupied | 1
步骤2-在合同之间清空房间 现在我们有了每个房间的合同和它们出现的顺序,我们可以在一个合同和下一个合同之间使用自联接来计算出它为空的日期范围。因此,选择该行,然后在与上一个排序器相同的roomid上连接到该行。在上表中,第1行将连接到第2行。这为我们提供了第1行的开始日期ExpireOn和第2行的结束日期active on。我们只需加/减一天,这样它们就不会重叠:

select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
    inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
步骤3-管理范围起点和终点的间隙 最后一步是如果一个房间在范围的开始是空的-这赢了;因为第一份合同没有上一行,所以不能包括在步骤1中

为此,我们只需要找到最早的占用日期,并将其用作空周期的到期日期。我们还检查这是在开始日期之后,所以对于一个实际占用的rom,我们不会得到在同一天开始和结束的条目

结束日期也适用于此-查找最大到期日并将结束日期用作结束日期:

select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
    having min(activeon) > @startDate
union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
    having max(expireson) < @enddate
对于空CTE和极端CTE,您可以在最终的联合中对它们进行子查询,但为了清晰起见,我将它们分开

; with occupieds as (
    SELECT Room.id,
           RentContract.activeon,
           RentContract.expireson,
           'Occupied' as [State],
           row_number() over (partition by roomid order by activeon) SortOrder
    FROM RentContract
    INNER JOIN Room ON RentContract.roomid = Room.id
    WHERE (RentContract.activeon >= @startDate
           OR RentContract.activeon IS NULL)
      AND (RentContract.expireson <= @endDate
           OR RentContract.expireson IS NULL)
)
select id, activeon, expireson, [State] from occupieds
    union all
select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
    inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
    union all
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
    having min(activeon) > @startDate
    union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
    having max(expireson) < @enddate
order by id, activeon

如果您发布具有预期输出的表数据会更好。您已经标记了mysql和sql server,就是这样吗?从CTE、日期表等的连续日期列表开始,然后左键连接到查询。为您的州标签添加丰富的COALECSE或IFNULL并烘焙…另外,请共享相关的表结构。RentContract是否同时包含activeon和new_activeon这样的列?如果是,它们有何不同?另外,确定时间间隔的标准是什么,特别是要动态显示的时间间隔?必须有一些逻辑才能生成它们。是的,请在表格中显示一些示例数据。谢谢。看起来很不错。我会结帐回来找你。非常感谢你的帮助。我对一个细节有一个问题,请检查我的编辑。对不起,最后一节有小错误。我们只需要检查最小日期是否大于@startDate而不是+/-1天。更新了。看起来不错。我会结帐回来找你。谢谢
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
    having min(activeon) > @startDate
union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
    having max(expireson) < @enddate
select id, activeon, expireson, [State] from occupieds
    union all
select id, activeon, expireson, [State] from empties
    union all
select id, activeon, expireson, [State] from extremes
order by id, activeon
; with occupieds as (
    SELECT Room.id,
           RentContract.activeon,
           RentContract.expireson,
           'Occupied' as [State],
           row_number() over (partition by roomid order by activeon) SortOrder
    FROM RentContract
    INNER JOIN Room ON RentContract.roomid = Room.id
    WHERE (RentContract.activeon >= @startDate
           OR RentContract.activeon IS NULL)
      AND (RentContract.expireson <= @endDate
           OR RentContract.expireson IS NULL)
)
select id, activeon, expireson, [State] from occupieds
    union all
select o1.id, o1.expireson + 1 as activeon, o2.activeon - 1 as expireson, 'Empty' as [State] from occupieds o1
    inner join occupieds o2 on o1.id = o2.id and o1.SortOrder = o2.SortOrder - 1
    union all
select id, @startDate as activeon, min(activeon) - 1 as expireson, 'Empty' as [State] from occupieds group by id
    having min(activeon) > @startDate
    union all
select id, max(expireson) + 1 as activeon, @endDate as expireson, 'Empty' as [State] from occupieds group by id
    having max(expireson) < @enddate
order by id, activeon