PostgreSQL:通过 LATERAL 连接关联的值总和
PostgreSQL: Sum of values related via LATERAL join
我正在尝试清理 PostgreSQL table 中的数据,其中一些记录在 email_address
列中有大量脏话(有问题的记录有由于一个已修复的错误而感到沮丧,因此被激动的用户输入):
┌───────────────────┐
│ email_address │
├───────────────────┤
│ foo@go.bar.me.net │
│ foo@foo.com │
│ foo@example.com │
│ baz@example.com │
│ barred@qux.com │
└───────────────────┘
所需的查询输出
我想构建一个查询,用脏话分数对数据 table 中的每一行进行注释,并按分数对记录进行排序,以便人们可以浏览带注释的数据(呈现在网络应用程序中)并采取必要的措施:
┌───────────────────┬───────┐
│ email_address │ score │
├───────────────────┼───────┤
│ foo@foo.com │ 18 │
│ foo@go.bar.me.net │ 14 │
│ foo@example.com │ 9 │
│ baz@example.com │ 3 │
│ barred@qux.com │ 0 │
└───────────────────┴───────┘
尝试 #1
我采用的方法是构建一个正则表达式列表(现在我有 2 个问题...)和分数,如果在 email_address栏目。我的 profanities
table 看起来像这样:
┌──────────────────┬───────┐
│ profanity_regexp │ score │
├──────────────────┼───────┤
│ foo │ 9 │
│ bar(?!red) │ 5 │
│ baz │ 3 │
└──────────────────┴───────┘
横向连接
我发现我可以在 regexp_matches
函数上使用 LATERAL
连接来从每个 email_address
中提取所有脏话(但没有脏话的记录会被丢弃):
SELECT
data.email_address,
array_agg(matches)
FROM
data,
profanities p,
LATERAL regexp_matches(data.email_address, p.posix_regexp, 'gi') matches
GROUP BY
data.email_address;
这会产生以下结果:
┌───────────────────┬───────────────────┐
│ email_address │ profanities_found │
├───────────────────┼───────────────────┤
│ foo@foo.com │ {{foo},{foo}} │
│ foo@example.com │ {{foo}} │
│ foo@go.bar.me.net │ {{foo},{bar}} │
│ baz@example.com │ {{baz}} │
└───────────────────┴───────────────────┘
SUB-SELECT
我还想出了如何使用此 SQL:
为每条记录获取一组亵渎分数小计
SELECT
data.email_address,
array(
SELECT score * (
SELECT COUNT(*)
FROM (SELECT
regexp_matches(data.email_address, p.posix_regexp, 'gi')
) matches
)
FROM profanities p
) prof
from data;
正确生成所有行(包括没有亵渎的行):
┌───────────────────┬──────────┐
│ email_address │ prof │
├───────────────────┼──────────┤
│ foo@go.bar.me.net │ {9,5,0} │
│ foo@foo.com │ {18,0,0} │
│ foo@example.com │ {9,0,0} │
│ baz@example.com │ {0,0,3} │
│ barred@qux.com │ {0,0,0} │
└───────────────────┴──────────┘
问题
如何对横向连接的结果求和以获得所需的输出?
我可以使用其他策略来获得所需的结果吗?
发布了这个问题的实时代码 fiddle
将另一个 select 添加到您的查询中。当前查询很好,但您只需要对数组求和即可。
SELECT email_address,
(
SELECT SUM(s)
FROM
UNNEST(prof.profanity_score_subtotals) s
) AS sum_prof FROM (
SELECT
data.email_address,
array(
SELECT score * (
SELECT COUNT(*)
FROM (SELECT
regexp_matches(data.email_address, p.profanity_regexp, 'gi')
) matches
)
FROM profanities p
) profanity_score_subtotals
FROM data
) prof;
出于某种原因,postgres 不允许您将 set-returning 函数用作 where 子句的一部分,因此您需要进行两个横向连接:
SELECT
data.email_address,
t.score
FROM
data,
LATERAL (
SELECT
coalesce(sum(s.score), 0) AS score
FROM
profanities,
LATERAL (
SELECT
profanities.score * array_length(
regexp_matches(
data.email_address,
profanities.profanity_regexp,
'gi'
),
1
) score
) s
) t;
我之前接受了@daurnimator 的回答,但后来发现不需要额外的 LATERAL
连接。这是我最终在我的应用程序中使用的内容:
SELECT
data.email_address,
(
SELECT
coalesce(sum(s.score), 0) AS score
FROM
profanities,
LATERAL (
SELECT
profanities.score * array_length(
regexp_matches(
data.email_address,
profanities.profanity_regexp,
'gi'
),
1
) score
) s
) AS score
FROM
data;
事实证明我的版本在查询中是 slightly faster since it avoids a nested loop。另一个优点是它可以在我的应用程序中用作 Django 的 RawSQL
函数的注释,然后允许我 order_by('-score')
并首先显示最亵渎的条目。
我正在尝试清理 PostgreSQL table 中的数据,其中一些记录在 email_address
列中有大量脏话(有问题的记录有由于一个已修复的错误而感到沮丧,因此被激动的用户输入):
┌───────────────────┐ │ email_address │ ├───────────────────┤ │ foo@go.bar.me.net │ │ foo@foo.com │ │ foo@example.com │ │ baz@example.com │ │ barred@qux.com │ └───────────────────┘
所需的查询输出
我想构建一个查询,用脏话分数对数据 table 中的每一行进行注释,并按分数对记录进行排序,以便人们可以浏览带注释的数据(呈现在网络应用程序中)并采取必要的措施:
┌───────────────────┬───────┐ │ email_address │ score │ ├───────────────────┼───────┤ │ foo@foo.com │ 18 │ │ foo@go.bar.me.net │ 14 │ │ foo@example.com │ 9 │ │ baz@example.com │ 3 │ │ barred@qux.com │ 0 │ └───────────────────┴───────┘
尝试 #1
我采用的方法是构建一个正则表达式列表(现在我有 2 个问题...)和分数,如果在 email_address栏目。我的 profanities
table 看起来像这样:
┌──────────────────┬───────┐ │ profanity_regexp │ score │ ├──────────────────┼───────┤ │ foo │ 9 │ │ bar(?!red) │ 5 │ │ baz │ 3 │ └──────────────────┴───────┘
横向连接
我发现我可以在 regexp_matches
函数上使用 LATERAL
连接来从每个 email_address
中提取所有脏话(但没有脏话的记录会被丢弃):
SELECT
data.email_address,
array_agg(matches)
FROM
data,
profanities p,
LATERAL regexp_matches(data.email_address, p.posix_regexp, 'gi') matches
GROUP BY
data.email_address;
这会产生以下结果:
┌───────────────────┬───────────────────┐ │ email_address │ profanities_found │ ├───────────────────┼───────────────────┤ │ foo@foo.com │ {{foo},{foo}} │ │ foo@example.com │ {{foo}} │ │ foo@go.bar.me.net │ {{foo},{bar}} │ │ baz@example.com │ {{baz}} │ └───────────────────┴───────────────────┘
SUB-SELECT
我还想出了如何使用此 SQL:
为每条记录获取一组亵渎分数小计SELECT
data.email_address,
array(
SELECT score * (
SELECT COUNT(*)
FROM (SELECT
regexp_matches(data.email_address, p.posix_regexp, 'gi')
) matches
)
FROM profanities p
) prof
from data;
正确生成所有行(包括没有亵渎的行):
┌───────────────────┬──────────┐ │ email_address │ prof │ ├───────────────────┼──────────┤ │ foo@go.bar.me.net │ {9,5,0} │ │ foo@foo.com │ {18,0,0} │ │ foo@example.com │ {9,0,0} │ │ baz@example.com │ {0,0,3} │ │ barred@qux.com │ {0,0,0} │ └───────────────────┴──────────┘
问题
如何对横向连接的结果求和以获得所需的输出?
我可以使用其他策略来获得所需的结果吗?
发布了这个问题的实时代码 fiddle
将另一个 select 添加到您的查询中。当前查询很好,但您只需要对数组求和即可。
SELECT email_address,
(
SELECT SUM(s)
FROM
UNNEST(prof.profanity_score_subtotals) s
) AS sum_prof FROM (
SELECT
data.email_address,
array(
SELECT score * (
SELECT COUNT(*)
FROM (SELECT
regexp_matches(data.email_address, p.profanity_regexp, 'gi')
) matches
)
FROM profanities p
) profanity_score_subtotals
FROM data
) prof;
出于某种原因,postgres 不允许您将 set-returning 函数用作 where 子句的一部分,因此您需要进行两个横向连接:
SELECT
data.email_address,
t.score
FROM
data,
LATERAL (
SELECT
coalesce(sum(s.score), 0) AS score
FROM
profanities,
LATERAL (
SELECT
profanities.score * array_length(
regexp_matches(
data.email_address,
profanities.profanity_regexp,
'gi'
),
1
) score
) s
) t;
我之前接受了@daurnimator 的回答,但后来发现不需要额外的 LATERAL
连接。这是我最终在我的应用程序中使用的内容:
SELECT
data.email_address,
(
SELECT
coalesce(sum(s.score), 0) AS score
FROM
profanities,
LATERAL (
SELECT
profanities.score * array_length(
regexp_matches(
data.email_address,
profanities.profanity_regexp,
'gi'
),
1
) score
) s
) AS score
FROM
data;
事实证明我的版本在查询中是 slightly faster since it avoids a nested loop。另一个优点是它可以在我的应用程序中用作 Django 的 RawSQL
函数的注释,然后允许我 order_by('-score')
并首先显示最亵渎的条目。