在我的数据库中,我有一个Reservation
表,它有三列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
有两行,并且每行都有日期,但第一行是第二行的日期间隔。总天数应为5天,因为第一天不应计算在第二天已经存在的天数。
发生这种情况的原因是每个房子都有两个房间,不同的人可以在同一个日期住在那个房子里。
我的问题是:如何省略这些案件,只计算房子被占用的真实日子?
答案 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
排序的,因此上一个开始日期总是小于或等于当前开始日期。 请注意,我们不需要预留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 |