我正在尝试在包含一堆非线性数据的日志样式表上运行一些查询。我有以下架构:
Signouts
+------------+----------------+------------+----------+
| signout_id | environment_id | date_start | date_end |
+------------+----------------+------------+----------+
| int | int | datetime | datetime |
+------------+----------------+------------+----------+
Environments
+-----+---------+
| id | name |
+-----+---------+
| int | varchar |
+-----+---------+
Signouts
是日志表(我说“日志表”因为记录永远不会更新,只会标记为“已禁用”并重新添加)。当用户注销环境时,他们选择的开始和结束时间将输入到注销表中。目前,要查看环境是否已注销,我只需检查当前日期是否介于date_start
和date_end
之间。如果其他用户想要退出该环境,他们可以选择的最短时间是当前退出的结束日期。
但我现在面临新的挑战。我现在需要实现预订系统。突然之间,日期可以在将来的任何地方,并且可以随时保留环境。现在我需要知道什么时候仍然可以注销一个环境,以及那些最小(现在最大)的值是什么!
我已经把它归结为这个天真的计划,但是我无法将其纳入SQL:
get all signouts where start < curdate & end > curdate
if there is no current signout, get the min start of all signouts where start > curdate
if there is a signout, get the max end
以下是许多其他报废查询中最接近的内容:
SELECT s.date_start_unavailable, s.date_available, e.id AS environment_id
FROM Environments AS e
LEFT OUTER JOIN (
SELECT TOP (100) PERCENT signout_id, environment_id, username, date_start, date_end, project, notes, in_use, max(date_end) as date_available, min(date_start) as date_start_unavailable
FROM dbo.Signouts
WHERE date_end >= GETDATE()
GROUP BY signout_id, environment_id, username, date_start, date_end, project, notes, in_use
ORDER BY date_start DESC
) AS s ON s.environment_id = e.id
这几乎有效。 date_start_unavailable
是系统无法进行退出的时间,dave_available
是不再有退出的时间。然而,这仍然存在问题;有人可以在未来几年保留一个月的环境,普通用户将无法看到大多数时间是未分配的。我必须找到一种方法来限制它,但我可以稍后再担心。
签名会持续用户输入的任意时间,否则实施时间阻止系统将是微不足道的。如果有人能提供一些DBA智慧,那将非常感激!
答案 0 :(得分:1)
像这样设置我的测试环境:
create table environment (id int, name varchar(255));
insert into environment values (1, 'DVD');
insert into environment values (2, 'BluRay');
create table signout (id int, environment_id int, date_start date, date_end date);
insert into signout values (1, 1, '01.11.2015', '09.11.2015');
insert into signout values (2, 1, '10.11.2015', '12.11.2015');
insert into signout values (3, 1, '01.12.2015', '24.12.2015');
insert into signout values (4, 2, '01.12.2015', '02.12.2015');
insert into signout values (5, 2, '04.12.2015', '07.12.2015');
insert into signout values (6, 2, '11.12.2015', '13.12.2015');
insert into signout values (7, 2, '14.12.2015', '23.12.2015');
现在,选择预订时间非常简单:
select e.name, s.date_start d_start, s.date_end d_end, 'booked' as d_status FROM
signout s inner join environment e ON e.id = s.environment_id
那么空闲时间呢?这些将是没有预订的时间 - 因此您按照给定的顺序加入表格:
select e.name, dateadd(DAY, 1, s.date_end) d_start,
COALESCE(dateadd(day, -1, s2.date_start), '31.12.2025') d_end, 'free'
FROM signout s
OUTER APPLY (
SELECT TOP 1 date_start, date_end from signout sx
WHERE sx.environment_id = s.environment_id
AND sx.date_start > s.date_end ORDER BY sx.date_start
) s2
inner join environment e ON e.id = s.environment_id
WHERE (s2.date_end is NULL OR s2.date_start > dateadd(DAY, 1, s.date_end))
现在将这些联合起来并根据环境和日期添加排序:
select e.name, s.date_start d_start, s.date_end d_end, 'booked' as d_status FROM
signout s inner join environment e ON e.id = s.environment_id
AND s.date_start > getdate()
UNION
select e.name, dateadd(DAY, 1, s.date_end) d_start,
COALESCE(dateadd(day, -1, s2.date_start), '31.12.2025') d_end, 'free'
FROM signout s
OUTER APPLY (
SELECT TOP 1 date_start, date_end from signout sx
WHERE sx.environment_id = s.environment_id
AND sx.date_start > s.date_end ORDER BY sx.date_start
) s2
inner join environment e ON e.id = s.environment_id
WHERE (s2.date_end is NULL OR s2.date_start > dateadd(DAY, 1, s.date_end))
AND s.date_start > getdate()
ORDER BY 1, 2
这就是我得到的:
BluRay 2015-12-01 2015-12-02 booked
BluRay 2015-12-03 2015-12-03 free
BluRay 2015-12-04 2015-12-07 booked
BluRay 2015-12-08 2015-12-10 free
BluRay 2015-12-11 2015-12-13 booked
BluRay 2015-12-14 2015-12-23 booked
BluRay 2015-12-24 2025-12-31 free
DVD 2015-11-10 2015-11-12 booked
DVD 2015-11-13 2015-11-30 free
DVD 2015-12-01 2015-12-24 booked
DVD 2015-12-25 2025-12-31 free