SQL:仅选择以给定间隔与其他结果分隔的值的行

Posted

技术标签:

【中文标题】SQL:仅选择以给定间隔与其他结果分隔的值的行【英文标题】:SQL: Select rows only with values separated by a given interval from other results 【发布时间】:2017-02-10 23:44:16 【问题描述】:

假设我有一个看起来像这样的表格,以秒为单位提供各种事件的时间。

event_time
----------
11
14
18
20
25
39
42
43
50

我正在尝试提出一个查询,该查询将为我提供该表中的一组行,其中每一行与 result 中的其他行至少相隔 10 秒。 p>

期望的结果是:

event_time
----------
11
25
39
50

包含event_time=11 的行是因为它前面没有任何内容。带有event_time=25 的行是下一个要返回的行,因为它是距离带有event_time=11 的行至少10 秒的第一行。

我正在使用 Postgres。我可以使用递归查询/CTE 来做到这一点,但如果不使用 ORDER BY、LIMIT 等子句就无法做到这一点,而且 Postgres 显然不允许在递归查询中使用这些。

【问题讨论】:

托管 SQL 的编程语言是什么?为什么要尝试用 SQL 来做呢? 因为我希望尽可能在数据库中对其进行评估,而不是依赖于通过网络传输整个数据集以在客户端上进行处理。 您需要为此使用递归 CTE。如果您有大量数据,在应用程序端执行此操作可能更有效。 【参考方案1】:

我可以使用递归查询/CTE 来做到这一点,但如果不使用 ORDER BY、LIMIT 等子句就无法做到这一点,而且 Postgres 显然不允许在递归查询中使用这些。

with recursive 
  t(x) as ( --Test data
    select * from unnest('11,14,18,20,25,39,42,43,50,55,60,61,69,70,71'::int[])
    order by random()),
  r(x) as (
    select min(x) as x from t
    union all
    (select t.x from t join r on (t.x >= r.x + 10) order by t.x limit 1))
select * from r;

http://rextester.com/VHOGH66762

但我个人更喜欢the solution with stored function。

【讨论】:

太棒了!这几乎正​​是我尝试过的,但没有括号围绕union all 之后的语句,它会以ERROR: LIMIT in a recursive query is not implemented 失败。 @avalys 实际上,union 子句很常见。只是比较:rextester.com/FUO17127 查询真的很好,值得接受。我只会添加某种警告,您不应该将它用于大型数据集。我的简单 plpgsql 解决方案会快很多倍。 @klin 如您所见,我在答案中提到我更喜欢您的解决方案。实际上,我的回答是针对所提供的报价,而不是针对整个问题。 @avalys 但是看看klin's solution - 它会更有效率。 rextester.com/BQBH89836 - 递归查询需要 2.62 秒,函数需要 0.6 秒。【参考方案2】:

您可以使用 plpgsql,这对于大型数据集来说似乎很简单且非常有效(与假设的递归查询相比)。

create or replace function strain_events()
returns setof events language plpgsql as $$
declare
    curr record;
    prev int;
begin
    for curr in
        select *
        from events
        order by 1
    loop
        if prev is null or curr.event_time >= prev + 10 then
            return next curr;
            prev = curr.event_time;
        end if;
    end loop;
end $$;

select * from strain_events();

【讨论】:

这也是我的想法。但是如果有一个事件正好相差 10,这就会中断。 因为我的第一次尝试不成功,我发布了一个替代解决方案。【参考方案3】:

我认为这个查询会起作用

select distinct  event_time_b
from
(
select event_time_a , min(event_time_b) event_time_b
from
(
select a.event_time event_time_a , b.event_time event_time_b , b.event_time-a.event_time diff
 from (select 0 as event_time union select event_time from  so_ques) a , so_ques b
where a.event_time<>b.event_time
and  b.event_time-a.event_time>=10
order by a.event_time
) a
group by event_time_a
order by event_time_a
) a
order by 1
;

表名 = so_ques(为测试而创建)

【讨论】:

也不起作用。试过了。检查这个案例:insert into so_ques values (11),(14),(18),(20),(25),(39),(42),(43),(50),(55), (60), (61), (69), (70), (71);

以上是关于SQL:仅选择以给定间隔与其他结果分隔的值的行的主要内容,如果未能解决你的问题,请参考以下文章

获取用逗号分隔的值的计数[重复]

SQL IN 子句仅返回以逗号分隔的 ID 列表中具有第一个匹配项的行

包含逗号分隔值的列中的值

连接表并选择给定 id 仅存在一个值的行

让 SQL 使用选择表单元素,逗号分隔

选择最接近的值小于给定值的条目