Select 排名靠前的重叠片段 SQL
Select top ranked overlapping segment SQL
我正在寻找 select 排名靠前的重叠片段的方法。 Table 看起来像这样:
CODE START STOP RANK
shift 2016-07-20 05:00 AM 2016-07-20 08:00 AM 5
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM 1
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM 2
这就是我希望输出的内容:
CODE START STOP
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM
shift 2016-07-20 05:08 AM 2016-07-20 06:00 AM
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM
shift 2016-07-20 06:30 AM 2016-07-20 08:00 AM
所以我只想看看排名靠前的部分是怎么说这个人的状态的,但是如果他们除了标准 "shift" 部分之外没有任何其他状态,那么只显示他们正在值班。
有道理吗?请解决任何问题或建议的解决方案。此刻我似乎什么也想不起来。我可以 select 排名靠前的部分,但当它们重叠时就不行。
编辑: 正如您在我的 desired 输出中看到的,shift 段被覆盖late 段从 05:00 AM 到 05:08 AM 具有更高的排名(数字越小意味着排名越高,通常在排名中),但是从 05:08 AM 因为没有段覆盖它,我们回到我们的默认段 shift 从 05:08 AM 到 06:00 AM.
然后有一个预定的 break 段从 06:00 AM 和 06:30 AM 再次覆盖 shift 分割。完成后,当 shift 结束时,我们返回默认段 shift 从 06:30 AM 到 08:00 AM .
我希望这是有道理的。
耶,一个 SQL 谜题,我无法抗拒! :D
这是一种可能的解决方案。我手边没有 SQL 服务器(使用 my favorite database :)),但 SQL 应该主要是 portable:
create or replace table ranges(
code varchar,
beg timestamp_ntz,
end timestamp_ntz,
rank integer);
insert into ranges values
('shift', '2016-07-20 05:00:00', '2016-07-20 08:00:00', 5),
('late', '2016-07-20 05:00:00', '2016-07-20 05:00:08', 1),
('break', '2016-07-20 06:00:00', '2016-07-20 06:30:00', 2);
WITH PERIODS AS (
select beg, lead(beg, 1) over (order by beg) AS end
from (select beg from ranges union select end from ranges)
),
MATCHING_RANGES AS (
select periods.beg, periods.end, ranges.code, ranges.rank
from periods
join ranges on (periods.beg >= ranges.beg and periods.end <= ranges.end)
where periods.end is not null
),
RANKED_RANGES AS (
select beg, end, code, row_number() over (partition by beg order by rank) in_period_rank
from matching_ranges
)
select code, beg, end from ranked_ranges
where in_period_rank = 1
order by beg;
-------+---------------------+---------------------+
CODE | BEG | END |
-------+---------------------+---------------------+
late | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 |
shift | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 |
break | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 |
shift | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 |
-------+---------------------+---------------------+
解释(我使用 "ranges" 表示您的原始 table,并使用 "periods" 表示其中的一部分,就像您在输出中想要的那样):
在 PERIODS 中,我们及时创建所有不同的时刻,并使用 LAG 查找下一个时刻。输出为:
---------------------+---------------------+
BEG | END |
---------------------+---------------------+
2016-07-20 05:00:00 | 2016-07-20 05:00:08 |
2016-07-20 05:00:08 | 2016-07-20 06:00:00 |
2016-07-20 06:00:00 | 2016-07-20 06:30:00 |
2016-07-20 06:30:00 | 2016-07-20 08:00:00 |
2016-07-20 08:00:00 | [NULL] |
---------------------+---------------------+
然后在 MATCHING_RANGES 中,对于每个 "period" 我们从定义的 table 中找到所有可能的范围(同时删除最后一行,NULL),输出:
---------------------+---------------------+-------+------+
BEG | END | CODE | RANK |
---------------------+---------------------+-------+------+
2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 5 |
2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1 |
2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 5 |
2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 5 |
2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 2 |
2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 5 |
---------------------+---------------------+-------+------+
请注意如何创建匹配
的范围和周期的所有组合
然后在 RANKED_RANGES 中,我们计算每一行在其周期内的优先级:
---------------------+---------------------+-------+----------------+
BEG | END | CODE | IN_PERIOD_RANK |
---------------------+---------------------+-------+----------------+
2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1 |
2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 2 |
2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 1 |
2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 1 |
2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 2 |
2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 1 |
---------------------+---------------------+-------+----------------+
然后我们简单地 select 排名为 1 的行 :)
我正在寻找 select 排名靠前的重叠片段的方法。 Table 看起来像这样:
CODE START STOP RANK
shift 2016-07-20 05:00 AM 2016-07-20 08:00 AM 5
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM 1
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM 2
这就是我希望输出的内容:
CODE START STOP
late 2016-07-20 05:00 AM 2016-07-20 05:08 AM
shift 2016-07-20 05:08 AM 2016-07-20 06:00 AM
break 2016-07-20 06:00 AM 2016-07-20 06:30 AM
shift 2016-07-20 06:30 AM 2016-07-20 08:00 AM
所以我只想看看排名靠前的部分是怎么说这个人的状态的,但是如果他们除了标准 "shift" 部分之外没有任何其他状态,那么只显示他们正在值班。
有道理吗?请解决任何问题或建议的解决方案。此刻我似乎什么也想不起来。我可以 select 排名靠前的部分,但当它们重叠时就不行。
编辑: 正如您在我的 desired 输出中看到的,shift 段被覆盖late 段从 05:00 AM 到 05:08 AM 具有更高的排名(数字越小意味着排名越高,通常在排名中),但是从 05:08 AM 因为没有段覆盖它,我们回到我们的默认段 shift 从 05:08 AM 到 06:00 AM.
然后有一个预定的 break 段从 06:00 AM 和 06:30 AM 再次覆盖 shift 分割。完成后,当 shift 结束时,我们返回默认段 shift 从 06:30 AM 到 08:00 AM .
我希望这是有道理的。
耶,一个 SQL 谜题,我无法抗拒! :D
这是一种可能的解决方案。我手边没有 SQL 服务器(使用 my favorite database :)),但 SQL 应该主要是 portable:
create or replace table ranges(
code varchar,
beg timestamp_ntz,
end timestamp_ntz,
rank integer);
insert into ranges values
('shift', '2016-07-20 05:00:00', '2016-07-20 08:00:00', 5),
('late', '2016-07-20 05:00:00', '2016-07-20 05:00:08', 1),
('break', '2016-07-20 06:00:00', '2016-07-20 06:30:00', 2);
WITH PERIODS AS (
select beg, lead(beg, 1) over (order by beg) AS end
from (select beg from ranges union select end from ranges)
),
MATCHING_RANGES AS (
select periods.beg, periods.end, ranges.code, ranges.rank
from periods
join ranges on (periods.beg >= ranges.beg and periods.end <= ranges.end)
where periods.end is not null
),
RANKED_RANGES AS (
select beg, end, code, row_number() over (partition by beg order by rank) in_period_rank
from matching_ranges
)
select code, beg, end from ranked_ranges
where in_period_rank = 1
order by beg;
-------+---------------------+---------------------+
CODE | BEG | END |
-------+---------------------+---------------------+
late | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 |
shift | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 |
break | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 |
shift | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 |
-------+---------------------+---------------------+
解释(我使用 "ranges" 表示您的原始 table,并使用 "periods" 表示其中的一部分,就像您在输出中想要的那样):
在 PERIODS 中,我们及时创建所有不同的时刻,并使用 LAG 查找下一个时刻。输出为:
---------------------+---------------------+ BEG | END | ---------------------+---------------------+ 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 | 2016-07-20 08:00:00 | [NULL] | ---------------------+---------------------+
然后在 MATCHING_RANGES 中,对于每个 "period" 我们从定义的 table 中找到所有可能的范围(同时删除最后一行,NULL),输出:
---------------------+---------------------+-------+------+ BEG | END | CODE | RANK | ---------------------+---------------------+-------+------+ 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 5 | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1 | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 5 | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 5 | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 2 | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 5 | ---------------------+---------------------+-------+------+
请注意如何创建匹配
的范围和周期的所有组合
然后在 RANKED_RANGES 中,我们计算每一行在其周期内的优先级:
---------------------+---------------------+-------+----------------+ BEG | END | CODE | IN_PERIOD_RANK | ---------------------+---------------------+-------+----------------+ 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late | 1 | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 2 | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 1 | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 1 | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 2 | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 1 | ---------------------+---------------------+-------+----------------+
然后我们简单地 select 排名为 1 的行 :)