丢弃结果中包含的现有日期SQL Server

时间:2017-09-14 20:35:52

标签: sql sql-server date

在我的数据库中,我有一个Reservation表,它有三列Initial DayLast DayHouse Id

我想计算总天数并省略重复的天数,例如:

+-------------+------------+------------+
|             | Results    |            |
+-------------+------------+------------+
|   House Id  | InitialDay |  LastDay   |
+-------------+------------+------------+
|  1          | 2017-09-18 | 2017-09-20 |
|  1          | 2017-09-18 | 2017-09-22 |
| 19          | 2017-09-18 | 2017-09-22 |
| 20          | 2017-09-18 | 2017-09-22 |
+-------------+------------+------------+

如果您注意到数字1的House Id有两行,并且每行都有日期,但第一行是第二行的日期间隔。总天数应为5天,因为第一天不应计算在第二天已经存在的天数。

发生这种情况的原因是每个房子都有两个房间,不同的人可以在同一个日期住在那个房子里。

我的问题是:如何省略这些案件,只计算房子被占用的真实日子?

4 个答案:

答案 0 :(得分:0)

select house_id,min(initialDay),max(LastDay)
group by houseId

如果我理解正确的话!

试试并告诉我它是如何为您服务的。

泰德。

答案 1 :(得分:0)

在思考你的问题时,我偶然发现了一个日历表的想法。您可以使用此代码创建一个代码,其中包含您希望日历的日期范围。代码来自http://blog.jontav.com/post/9380766884/calendar-tables-are-incredibly-useful-in-sql

declare @start_dt as date = '1/1/2010';     
declare @end_dt as date = '1/1/2020';       

declare @dates as table (
 date_id date primary key,
 date_year smallint,
 date_month tinyint,
 date_day tinyint,
 weekday_id tinyint,
 weekday_nm varchar(10),
 month_nm varchar(10),
 day_of_year smallint,
 quarter_id tinyint,
 first_day_of_month date,
 last_day_of_month date,
 start_dts datetime,
 end_dts datetime
)

while @start_dt < @end_dt
begin
    insert into @dates(
        date_id, date_year, date_month, date_day, 
        weekday_id, weekday_nm, month_nm, day_of_year, quarter_id, 
        first_day_of_month, last_day_of_month, 
        start_dts, end_dts
    )   
    values(
        @start_dt, year(@start_dt), month(@start_dt), day(@start_dt), 
        datepart(weekday, @start_dt), datename(weekday, @start_dt), datename(month, @start_dt), datepart(dayofyear, @start_dt), datepart(quarter, @start_dt),
        dateadd(day,-(day(@start_dt)-1),@start_dt), dateadd(day,-(day(dateadd(month,1,@start_dt))),dateadd(month,1,@start_dt)), 
        cast(@start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, @start_dt) as datetime))
    )
    set @start_dt = dateadd(day, 1, @start_dt)
end

select * 
into Calendar
from @dates

有了日历表后,您的查询就像:

一样简单
select distinct t.House_id, c.date_id
from Reservation as r
inner join Calendar as c
on 
    c.date_id >= r.InitialDay 
    and c.date_id <= r.LastDay

每个房间被占用的每个独特日都会给你一行。如果您需要每个房间被占用多少天的总和,那就变成了:

select a.House_id, count(a.House_id) as Days_occupied
from
    (select distinct t.House_id, c.date_id
    from so_test as t
    inner join Calendar as c
    on 
        c.date_id >= t.InitialDay 
        and c.date_id <= t.LastDay) as a
group by a.House_id

答案 2 :(得分:0)

创建一个包含所有可能日期的表格,然后将其加入“预订”表格,以便您拥有InitialDay和LastDay之间所有日期的列表。像这样:

    DECLARE @i date
DECLARE @last date
CREATE TABLE #temp (Date date)
SELECT @i = MIN(Date) FROM Reservations
SELECT @last = MAX(Date) FROM Reservations
WHILE @i <= @last
BEGIN
    INSERT INTO #temp VALUES(@i)
    SET @i = DATEADD(day, 1, @i)
END
SELECT HouseID, COUNT(*) FROM
(
    SELECT DISTINCT HouseID, Date FROM Reservation
    LEFT JOIN #temp 
        ON Reservation.InitialDay <= #temp.Date 
        AND Reservation.LastDay >= #temp.Date
) AS a
GROUP BY HouseID
DROP TABLE #temp

答案 3 :(得分:0)

在使用SQL Server 2012或更高版本时,您可以使用LAG()获取上一个最终日期并调整初始日期:

with ReservationAdjusted as (
select *,
  lag(LastDay) over(partition by HouseID order by InitialDay, LastDay) as PreviousLast
from Reservation
)
select HouseId,
  sum(case when PreviousLast>LastDay then 0 -- fully contained in the previous reservation
    when PreviousLast>=InitialDay then datediff(day,PreviousLast,LastDay) -- overlap
    else datediff(day,InitialDay,LastDay)+1 -- no overlap
    end) as Days
from ReservationAdjusted
group by HouseId

案例是:

  • 预订完全包含在之前的预订中:我们只需要比较结束日期​​,因为上一行是按InitialDay, LastDay排序的,因此上一个开始日期总是小于或等于当前开始日期。
  • 当前预订与之前的预约重叠:在这种情况下,我们调整开始并且不加1(初始日期已经计算),这种情况包括前一个末尾等于当前开始时(是一天重叠)。
  • 没有重叠:我们只计算差异,并在初始日加上1来计算。

请注意,我们不需要预留HouseID的额外条件,因为默认情况下LAG()函数会在前一行没有的情况下返回NULL,并且与null的比较总是错误的。

示例输入和输出:

| HouseId | InitialDay |    LastDay |
|---------|------------|------------|
|       1 | 2017-09-18 | 2017-09-20 |
|       1 | 2017-09-18 | 2017-09-22 |
|       1 | 2017-09-21 | 2017-09-22 |
|      19 | 2017-09-18 | 2017-09-27 |
|      19 | 2017-09-24 | 2017-09-26 |
|      19 | 2017-09-29 | 2017-09-30 |
|      20 | 2017-09-19 | 2017-09-22 |
|      20 | 2017-09-22 | 2017-09-26 |
|      20 | 2017-09-24 | 2017-09-27 |

| HouseId | Days |
|---------|------|
|       1 |    5 |
|      19 |   12 |
|      20 |    9 |