4

我正在尝试清理 PostgreSQL 表中的数据,其中一些记录在email_address(由于已修复的错误导致的挫败感,有问题的记录是由激动的用户输入的):

    ┌────────────────────┐
    │ email_address │
    ├────────────────────┤
    │ foo@go.bar.me.net │
    │ foo@foo.com │
    │ foo@example.com │
    │ baz@example.com │
    │ barred@qux.com │
    └────────────────────┘

所需的查询输出

我想构建一个查询,用亵渎分数注释数据表中的每一行,并按分数对记录进行排序,以便人类可以浏览带注释的数据(在网络应用程序中呈现)并采取必要的行动:

    ┌────────────────────┬────────┐
    │ email_address │ 分数 │
    ├────────────────────┼────────┤
    │ 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桌子看起来像这样:

    ┌──────────────────┬────────┐
    │ profanity_regexp │ 分数 │
    ├──────────────────┼────────┤
    │ foo │ 9 │
    │ bar(?!red) │ 5 │
    │ 巴兹 │ 3 │
    └──────────────────┴────────┘

横向连接

我发现我可以在函数上使用LATERAL连接regexp_matches来从每个函数中提取所有脏话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}} │
    └────────────────────┴────────────────────┘

子选择

我还想出了如何使用以下 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 │ 教授 │
    ├────────────────────┼──────────┤
    │ 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} │
    └────────────────────┴──────────┘

问题

如何对横向连接的结果求和以获得所需的输出?

我可以使用另一种策略来获得所需的结果吗?


我已经在http://sqlfiddle.com/#!17/6685c/4上为这个问题发布了一个实时代码小提琴

4

3 回答 3

1

出于某种原因,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;
于 2018-03-07T13:39:59.560 回答
1

将另一个选择添加到您的查询中。当前查询很好,但您只需要对数组求和。

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;
于 2018-03-07T07:46:52.617 回答
0

我之前接受了@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;

事实证明我的版本稍微快一些,因为它避免了查询中的嵌套循环。另一个优点是它可以在我的应用程序中用作 DjangoRawSQL函数的注释,允许我首先order_by('-score')显示最亵渎的条目。

于 2018-03-08T06:38:48.420 回答