将包含子字符串的多行折叠成一行

Posted

技术标签:

【中文标题】将包含子字符串的多行折叠成一行【英文标题】:Collapsing multiple rows containing substrings into a single row 【发布时间】:2018-09-21 07:04:11 【问题描述】:

我有几行记录 (id, query,count),我想通过观察多行上的 query 来折叠它们。我想保留最长的行 query 和折叠行的 count 字段的总和。

示例输入:

24, que, 2
24, querie, 1
24, queries, 1
25, term1, 3
25, term1+term2, 11
25, term1+term2+term3, 1
26, inventory, 5
26, issues, 10
27, close, 1
27, sclosed, 2
28, abcde, 2
28, abcfe, 2

需要的输出:

24, queries, 4
25, term1+term2+term3, 15
26, inventory, 5
26, issues, 10
27, close, 1
27, sclosed, 2
28, abcde, 2
28, abcfe, 2

我只关注子字符串的特殊情况:24,25 已折叠,但 27 未折叠,因为 close 上的前缀。 26 也不会折叠,因为第二行中的 query 字段不是第一行的子字符串(没有前缀)。

编辑:添加了 id 28,这是另一种不应折叠记录的情况。

【问题讨论】:

【参考方案1】:

demo:db<>fiddle

对于更一般的情况(例如,“差异可能在第 10 个字母”“有一行只有一个字符”),您需要确定正确的群体。所以有必要根据下一行检查行:“当前行是下一行的开始吗?”

使用substring 之类的东西,您可以检查开头的特殊长度(“将所有以相同 3 个字母开头的文本分组”但是如果您没有 3 个字母怎么办?或者差异在稍后的某个地方?)

这就是我在lag窗口函数(https://www.postgresql.org/docs/current/static/tutorial-window.html)的帮助下计算特殊组的原因:

SELECT 
    max(id) as id,                                        -- C
    max(phrase) as phrase,
    sum("count") as count
FROM (
    SELECT 
        *,
        SUM(is_diff) OVER (ORDER BY id, phrase) as ranked -- B
    FROM (
        SELECT
            *,
            -- A: 
            CASE WHEN phrase LIKE (lag(phrase) over (order by id, phrase)) || '%' THEN 0 ELSE 1 END as is_diff
        FROM phrases 
    )s
) s
GROUP BY ranked
ORDER BY ranked

主要思想讨论here。

答:lag 函数允许检查下一行的值。因此,如果当前行的phrase 是下一行的phrase 的开头,那么它们在同一个组中。 (current_row LIKE (next_row || '%'))。这是因为id 组按phrase 文本(及其长度)排序。

如果行不兼容,辅助变量设置为1,否则设置为0

B:可以添加辅助变量并生成组。 (有关详细信息,请参阅上面提供的链接)。

C:剩下的就是根据新生成的组值进行简单分组。

【讨论】:

这正是我所追求的,对于 abcdeabcfe 的附加案例来说非常棒。也会将其添加到我的问题中。没有意识到这些一般情况,谢谢!【参考方案2】:

如果您需要 agreagtio 作为 id,那么您可以使用 sum 来获取总数,使用 max 来获取名称

    select  id, max(col2), sum(col3)
    from my_table 
    group by  id 

如果您需要通过 id 进行聚合的 expectio 要获得 27 个折叠,您可以使用

    select  id, max(col2), sum(col3)
    from my_table 
    where id <>27
    group by  id 
    union  all
    select  id, col2, sum(col3)
    from my_table 
    where id =27
    group by  id, cold2
    order by id

【讨论】:

绝妙的技巧,但记录 26,27 将与两个字符串的最大值一起折叠。 @kami 这是有逻辑的还是只是一个没有动机的执行?..尝试解释获得这个的逻辑 表格中的大部分案例都是 26、27 的形式,我不需要折叠。查询是搜索词,24、25 是查询建议。我想折叠它们并只存储最长的查询,因为这是我唯一感兴趣的。【参考方案3】:

对 max 和 sum 使用聚合以及按列分组应该是 id 和 substring(column2,from 1 for 3),因为您遵循的是总值的某种模式

select id, max(column2), sum(column3)
from tablename
group by id, substring(column2,from 1 for 3)

【讨论】:

【参考方案4】:

这个任务不能用聚合来解决 - 因为在你的情况下,因为分组子集是由相等定义的,你想使用子字符串操作。

所以你需要实现自己的集合返回函数:

create table foo(id int, query text, count int);

CREATE OR REPLACE FUNCTION public.reduce()
 RETURNS SETOF foo
 LANGUAGE plpgsql
AS $function$
declare r foo; sr foo;
begin
  for r in select * from foo order by id, query
  loop
    if sr.id is null then
      sr := r;
    else
      if sr.id = r.id then
        if r.query like  sr.query || '%'
           or sr.query like r.query || '%' 
        then
          if length(r.query) > length(sr.query) then
            sr.query := trim(r.query);
          end if;
          sr.count := sr.count + r.count; 
        else
          return next sr;
          sr := r;
        end if;
      else
        return next sr;
        sr = r;
      end if;
    end if;
  end loop;
  if sr.id is not null then
    return next sr;
  end if;
end;
$function$

postgres=# select * from reduce();
+----+-------------------+-------+
| id |       query       | count |
+----+-------------------+-------+
| 24 | queries           |     4 |
| 25 | term1+term2+term3 |    15 |
| 26 | inventory         |     5 |
| 26 | issues            |    10 |
| 27 | close             |     1 |
| 27 | sclosed           |     2 |
+----+-------------------+-------+
(6 rows)

字符串操作可能会很慢,但是这个任务非常不相关,仅使用 SQL 是不可能的。

【讨论】:

【参考方案5】:
select  id, max(col2), sum(col3)
    from tablename
    where id in (24,25)
    group by  id 
    union  all
    select  id, col2, sum(col3)
    from tablename
    where id not in (24,25)
    group by  id,col3
    order by id,3;

【讨论】:

以上是关于将包含子字符串的多行折叠成一行的主要内容,如果未能解决你的问题,请参考以下文章

将一行拆分为多行(固定宽度)

最长不重复子串

将多个子模块折叠成一个 Cython 扩展

Oracle sql使用子查询将多行结果分组为一行

Logback - 如何编写自定义异常转换器以将堆栈跟踪折叠成一行

60剑指offer--把二叉树打印成多行