【问题标题】:Looking for a simpler alternative to a recursive query寻找递归查询的更简单替代方案
【发布时间】:2016-04-07 23:06:29
【问题描述】:

实际查询更多,但我面临的问题可以提炼为:

过滤单调递增整数行集的查询,以便 - 在最终结果集中,row(n+1).value >= row(n).value + 5 .

对于我需要解决的实际问题,行集计数在1000s。

举几个例子来说明:

  • 如果行是:1,2,3,4,5:那么查询应该返回:1
  • 如果行是:1,5,7,10,11,12,13:那么查询应该返回:1,7,12
  • 如果行是:6,8,11,16,20,23:那么查询应该返回:6,11,16,23
  • 如果行是:6,8,12,16,20,23:那么查询应该返回:6,12,20

我已经设法通过以下查询获得所需的结果,但它似乎过于复杂。取消注释不同的“..with t(k)..”以试用它们。

我正在寻找任何简化或替代方法来获得相同的结果。

    with recursive r(n, pri) as (
    with t(k) as (values (1),(2),(3),(4),(5))   -- the data we want to filter
    -- with t(k) as (values (1),(5),(7),(10),(11),(12),(13))
    -- with t(k) as (values (6),(8),(11),(16),(20),(23))
    -- with t(k) as (values (6),(8),(12),(16),(20),(23))
    select min(k), 1::bigint from t             -- bootstrap for recursive processing. 1 here represents rank().
    UNION
    select k, (rank() over(order by k)) rr      -- rank() is required just to filter out the rows we dont want from the final result set, and no other reason
    from r, t 
    where t.k >= r.n+5 and r.pri = 1            -- capture the rows we want, AND unfortunately a bunch of rows we dont want 
)
select n from r where pri = 1;                  -- filter out the rows we dont want

【问题讨论】:

  • 你不是说:row(n-2) <= row(n) -5 吗?例如:为什么输出中第二个示例中的7。因为1 在第一个位置?
  • 好的,我明白了;间隙条件基于 result 集。

标签: postgresql common-table-expression recursive-query


【解决方案1】:
-- The data:
CREATE TABLE rowseq(val INTEGER NOT NULL) ;
INSERT INTO rowseq(val ) values
    -- (1),(2),(3),(4),(5)
         (1), (5), (7), (10), (11), (12), (13)
    --(6),(8),(11),(16),(20),(23)
    --(6),(8),(12),(16),(20),(23)
        ;

        -- need this view, because a recursive CTE cannot be based on a CTE
        -- [we could also duplicate the row_number() in both legs of the recursive CTE]
CREATE TEMP VIEW qqq AS
        SELECT val, row_number() OVER (ORDER BY val) AS rn
        FROM rowseq
        ;

WITH RECURSIVE rrr AS (
        SELECT qqq.val, qqq.rn
        FROM qqq
        WHERE qqq.rn = 1
UNION
        SELECT q1.val, q1.rn
        FROM rrr
        JOIN qqq q1 ON q1.rn > rrr.rn AND q1.val >= rrr.val+5 -- The "Gap" condition
                AND NOT EXISTS ( SELECT * FROM  qqq nx                  -- But it must be the FISRT match
                        WHERE nx.rn > rrr.rn AND nx.val >= rrr.val+5    -- same condition
                        AND nx.rn < q1.rn                               -- but NO earlier match
                        )
        )
-- prove it to the world!
SELECT *
FROM rrr
        ;

【讨论】:

  • 谢谢!这个实现与我原来的相比有什么专业人士吗?猜猜权衡是 'rank() over' vs 'not exist (select..'
  • 恕我直言,递归查询的 终端 部分中的 min(k) 将始终选择相同的值,与递归分支无关。
【解决方案2】:

使用 plpgsql 怎么样?

drop table if exists t;
create table t(k) as (
  values 
    (1),(2),(3),(4),(5)
    --(1),(5),(7),(10),(11),(12),(13)
    --(6),(8),(11),(16),(20),(23)
    --(6),(8),(12),(16),(20),(23)
);

create or replace function foo(in n int, out k int) returns setof int as $$
declare
  r t;
  rp t;
begin
  rp := null;
  for r in (select * from t) loop
    if (rp is null) or (r.k >= rp.k + n) then
      rp := r;
      k := r.k;
      return next;
    end if;
  end loop;
  return;
end; $$ immutable language plpgsql;

select * from foo(5);

【讨论】:

  • 对不起,应该在我原来的问题中提到我正在寻找一种非游标/for循环方法。这个(for循环)是我目前拥有的。我正在寻找一个不错的纯 sql 选项,以便比较两者的相对性能。
  • 虽然,我喜欢你的光标 impl。比我拥有的更好,所以我会一直使用它,直到找到更好的 sql impl。谢谢!
  • @sr33 IMO 你应该在头制动(但可能更高效)纯 SQL 和更合乎逻辑的方法使用游标之间做出决定。实际上,正确的方法是调整数据模型以完全避免此类问题。国际海事组织。
猜你喜欢
  • 2017-11-29
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 2011-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多