在具有特定值的连续行上定义一个窗口

Posted

技术标签:

【中文标题】在具有特定值的连续行上定义一个窗口【英文标题】:Define a window over consecutive rows with a particular value 【发布时间】:2018-02-05 18:05:14 【问题描述】:

编辑 2 当您拥有的是按 <SORTING> 排序的表时,要记住的解决方案是 count( <GROUP-START-COND> or null ) over ( order by <SORTING> ) AS groupnr,新的行组由 <GROUP-START-COND> 发出信号,并且您需要一个计数器随着每组行的增加而增加,并且在整个组中保持不变。


原始问题

给定这样的表格:

╔════════╤═══════╤═══════╤════════════════════════════════════...
║ linenr │ level │  key  │ value                              ...
╠════════╪═══════╪═══════╪════════════════════════════════════...
║      9 │     1 │ title │ Text processing umbrella: Parse / T...
║     10 │     1 │ tags  │ text-processing typesetting markdow...
║     11 │     1 │ about │ unified is an interface for process...
║     12 │     2 │ ...   │ and rehype, but it also allows for ...
║     13 │     1 │ note  │ EXAMPLE                            ...
║     16 │     1 │ tags  │ foo bar baz                        ...
║     17 │     1 │ tags  │ ctx/tag spaceships/orville         ...
...

我如何定义一个window,它允许我在属于同一组的所有values 上使用array_aggregate,其中组定义为具有相邻linenrs 的行,其中第一行具有@ 987654331@ 和 key,以下行有 level = 2key = '...'(仅使用其中一个条件就足够了)。

我正在尝试提出一个涉及over ( ... rows between current row ... ) 的公式,但被卡住了;也许先对组进行编号,然后对组编号进行聚合将是一个很好的解决方案。

编辑我意识到我的问题可能不是最清楚的,缺少一个可行的示例,也许最好将其发布到 dba.stackexchange.com,因此以下是改进后的版本供您参考:


更新问题

我有下表的数据,如下所示; linenrs 单调递增但不一定连续;当key 字段包含省略号... 表示从上面继续的条目时:

create table source (
  linenr    integer unique not null,
  key       text not null,
  value     text );

insert into source values
  (  2, 'tags',  'a'          ),
  (  3, '...',   'b'          ),
  (  4, 'title', 'The Title'  ),
  (  5, 'note',  'this is'    ),
  (  6, '...',   'an EXAMPLE' ),
  (  8, 'title', 'over'       ),
  (  9, '...',   'three'      ),
  ( 10, '...',   'lines'      ),
  ( 11, 'about', 'grouping'   );

现在我想要查看根据key 字段的内容分配组号的视图;组号不必是连续的,但对于以... 以外的键开头并贯穿key... 的所有行的每组行应该是不同的,如下所示:

╔════════╤═══════╤═══════╤════════════╗
║ linenr │ group │  key  │   value    ║
╠════════╪═══════╪═══════╪════════════╣
║      2 │     1 │ tags  │ a          ║
║      3 │     1 │ ...   │ b          ║
║      4 │     2 │ title │ The Title  ║
║      5 │     3 │ note  │ this is    ║
║      6 │     3 │ ...   │ an EXAMPLE ║
║      8 │     4 │ title │ over       ║
║      9 │     4 │ ...   │ three      ║
║     10 │     4 │ ...   │ lines      ║
║     11 │     5 │ about │ grouping   ║
╚════════╧═══════╧═══════╧════════════╝

我尝试使用 windows/partitions 和 tabibitosan 模式来做到这一点,但无法提出任何可行的方法;此外,在lag() 的前一行使用在... 有多个连续行的情况下也无济于事。在电子表格中,这是一件非常容易的事情,但在 SQL 中,我似乎无法引用 current 查询的前一行,可以吗?


解决方案讨论

原来有一个解决方案如此简单,令人痛苦(不是自己想出来的):

select
    linenr                              as linenr,
    key                                 as key,
    value                               as value,
    sum( rst ) over ( order by linenr ) as group_nr
  from ( select
    linenr,
    key,
    value,
    case when key != '...' then 1 end   as rst
  from source ) as x;

这是可行的,因为我们为开始组的行分配了1,否则为null;然后,sum()ming 在所有行上(以正确的顺序)会将nulls 视为零,然后导致所有组起始行获得一个新的group_id,并且所有后续行都保持该计数。如果你知道怎么做就很简单......

积分归用户McNets。

这同样可以在一个简短而令人难忘的单行中完成,参见上面的编辑和下面Erwin Brandstetter 的答案。

EDIT 2 评论者理所当然地抱怨我编辑的问题确实是一个新问题。我认为结果是,在解决顽固问题时,应该尝试找到不会转动的特定螺钉,并提出一个小规模模型,突出该特定部分而忽略其他部分。在这种情况下,聚合具有组号的行对我来说并不是困难的部分,而是分配组号。此外,“定义一个允许我对所有值进行数组聚合的窗口”不是问题的一部分,它是我想象的可能导致解决方案的一部分。

【问题讨论】:

您的 Postgres 版本和表定义?假设linenr 被定义为唯一?单行(没有以下级别 2)是否也被视为组? 这里是 Postgres v10;我想我的编辑回答了你的问题。 你用另一个问题替换了问题。 @Erwin Brandstetter 我猜你可能会这么说;我意识到包含level 字段和聚合步骤都让人分心,而不是澄清。我会重新编辑我的问题,说这么多。道歉。 所以我们可以在已回答的情况下勾选这个? 【参考方案1】:

回答更新(新)问题:

SELECT *
     , count(key <> '...' OR NULL) OVER (ORDER BY linenr) AS grp
FROM   source;

在 dba.SE 上查看欺骗:

How can I assign group IDs depending on content?

回答原问题:

假设当前 Postgres 10 和 linenr 定义为 UNIQUE,这将实现您所描述的:

SELECT min(linenr) AS lines_from
     , max(linenr) AS lines_to
     , array_agg(value) AS value_arr
FROM  (
   SELECT linenr, level, value
        , count(level = 1 OR NULL) OVER (ORDER BY linenr) AS grp
        , row_number() OVER (ORDER BY linenr) - linenr    AS adjacent
   FROM   tbl
   ORDER  BY linenr
   ) sub
GROUP  BY grp, adjacent  -- same group, adjacent numbers
HAVING min(level) = 1    -- but only groups that start with level 1 
ORDER  BY lines_from;

SQL Fiddle 带有扩展测试用例。

相关,有更多解释:

Select longest continuous sequence

关于count(level = 1 OR NULL)

For absolute performance, is SUM faster or COUNT?

【讨论】:

【参考方案2】:

这可以使用 Tabibitosan 方法完成

select array_agg(value) FROM
(
select t.*, row_number() OVER (ORDER BY linenr ) - 
       row_number() OVER (PARTITION BY CASE WHEN level = 2 and key = '...'
                          THEN 1 ELSE 0 END ORDER BY linenr ) as chg
FROM   Table1 as t
  ) as a 
  WHERE (level,key)  <> ( 2,'...')
  GROUP BY chg
  ORDER BY chg;  

【讨论】:

我认为这个查询没有达到问题的要求。您的 row_number() OVER -- 1 or 0 恰好创建了两个组。似乎没有意义。 我也是:sqlfiddle.com/#!17/21a1ea/4“它有效”,好的。但它是否符合问题的要求? @ErwinBrandstetter :我想我可能误解了这个问题。但不清楚是否是这种情况。 这个问题确实留下了一些解释空间......希望OP会澄清。 感谢您提供有关 tabibitosan 方法的提示,但该方法在这里不起作用,或者至少不是您写下的方式(我也找不到其他方法),因为它没有如果我理解正确,不会产生一系列“差距和岛屿”。相反,有一个巧妙简单的解决方案,有人刚刚在这里指出了我:dba.stackexchange.com/a/197126/126933

以上是关于在具有特定值的连续行上定义一个窗口的主要内容,如果未能解决你的问题,请参考以下文章

SQL:检测具有相同键的连续行的连续块

MySQL 在特定条件下返回最新和最长的连续行

删除同一列或连续行的另一列中具有特定值和缺失值的行

基于正则表达式连接熊猫中的连续行

如何根据条件选择R数据框中的连续行?

计算同一列之间的差异,在python中由另一列分组的连续行