2017-09-14 13 views
1

私のデータベースにはReservationというテーブルがあり、それは3つのカラムInitial Day,Last DayおよびHouse 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に気づいた場合には、2つの行を持ち、それぞれの行は、日付が、最初の行を持っています2行目の日付の間隔にあります。合計日数は5でなければなりません。なぜなら、第1のものは第2のものに既に存在する日数として数えるべきではないからです。

これが起こっている理由は、各家に2つの部屋があり、異なる人が同じ日にその家に滞在できることです。

私の質問は、どうすればこれらのケースを省略でき、家が占領された実際の日数だけをカウントできますか?

+0

あなたはまた、 '1、2017年8月20日のようなレコードを持つことができ2017-08-22'、あなたはそれらの日は合計に追加したいと思います家1のために? –

+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を加えて最初の日もカウントします。

我々は前の行がない場合LAG()関数がNULLを返し、ヌルとの比較は常にfalseですので、デフォルトでHouseIDの予約のための余分な条件を必要としないでください。

サンプル入力と出力:私はそれを取る

| 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 

私が正しく理解していれば!

試してみて、それがどのように機能するか教えてください。

Ted。

+3

特定のhouseIDに複数のエントリがあり、それらの間に占有されていない時間がある場合、これはオーバーカウントされます。例えば。家1が9/1〜9/3の滞在と9/5〜9/10の滞在を持っている場合。 – Rominus

0

あなたの疑問を考えながら、私はカレンダーテーブルのアイデアを思いつきました。このコードを使用して、カレンダーに必要な日付の範囲を指定して、このコードを作成します。あなたに各部屋が占領されたそれぞれのユニークな日のための行を与える

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 

:あなたはあなたのクエリが同じくらい簡単ですカレンダーのテーブルを作成したら、コードは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 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 
関連する問題