2017-09-14 20 views
1

在我的數據庫中,我有一個Reservation表,它有三列Initial Day,Last DayHouse Id捨棄結果中包含的現有日期,SQL Server

我想算的總天數,而忽略那些重複誰,例如:

+-------------+------------+------------+ 
|    | 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 | 
+-------------+------------+------------+ 

如果您發現該House Id與1號有兩行,每行有日期,但第一排位於第二行的日期間隔內。總的來說,天數應該是5,因爲第一天不應該算作第二天已經存在的天數。

發生這種情況的原因是,每個房子有兩個房間,不同的人可以在同一日期住在那個房子裏。

我的問題是:我怎麼能省略這些情況,並只計算房子被佔領的真實日子?

+0

我想你也可以有類似'1,2017年8月20日的記錄,2017-08-22',你想那些日子添加到總爲房子嗎? –

+0

那麼輸出是什麼?房子1或... 5天?並有任何其他列? – scsimon

回答

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獲得的,所以上一個開始日期始終爲mino r或等於當前開始日期。
  • 當前預留與前一個重疊:在這種情況下,我們調整開始並且不加1(初始日期已經被計算),這種情況包括當前一個結束等於當前開始時(是一個日重疊)。
  • 沒有重疊:我們只是計算差異並加1來計算最初的一天。

請注意,我們並不需要額外的條件爲HouseID的保留,因爲在默認情況下LAG()函數返回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 | 
+0

這正是我的意思,它也很容易和簡單的閱讀,優秀的代碼非常感謝你。 – deduardolv

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

如果我理解正確!

試試吧,讓我知道它是如何爲你工作的。

泰德。

+3

如果某個特定houseID有多個條目,並且兩個條目之間沒有空閒時間,則會計入過多。例如。如果1號住宅從9/1到9/3,另一個從9/5到9/10。 – Rominus

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 
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 
相關問題