将包含子字符串的多行折叠成一行
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:剩下的就是根据新生成的组值进行简单分组。
【讨论】:
这正是我所追求的,对于abcde
和 abcfe
的附加案例来说非常棒。也会将其添加到我的问题中。没有意识到这些一般情况,谢谢!【参考方案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;
【讨论】:
以上是关于将包含子字符串的多行折叠成一行的主要内容,如果未能解决你的问题,请参考以下文章