将包含子字符串的多行折叠为单行

时间:2018-09-21 07:04:12

标签: sql postgresql

我有几行记录(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,这是另一种不应折叠记录的情况。

5 个答案:

答案 0 :(得分:4)

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

A:lag函数允许检查下一行的值。因此,如果当前行的phrase是下一行phrase的开始,则它们在同一组中。 (current_row LIKE (next_row || '%'))。之所以有效,是因为id组按phrase个文本(及其长度)排序。

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

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

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

答案 1 :(得分:0)

将聚合用于最大值和总和,并按列分组应为id和substring(column2,从1到3),因为您遵循某种总价值模式

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

答案 2 :(得分:0)

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

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

如果您需要按ID进行汇总的期望 并获得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

答案 3 :(得分:0)

使用聚合无法解决此任务-因为在您的情况下,因为分组子集由相等定义,并且您要使用子字符串操作。

因此您需要实现自己的集合返回函数:

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是不可能的。

答案 4 :(得分:-1)

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;