如何在 Postgresql 窗口函数的 PARTITION BY 中包含当前行

Posted

技术标签:

【中文标题】如何在 Postgresql 窗口函数的 PARTITION BY 中包含当前行【英文标题】:How to include current row in PARTITION BY of Postgresql's window function 【发布时间】:2019-10-09 20:56:26 【问题描述】:

我正在尝试执行以下操作;假设我想在给定条件下将表分区为两个分区:

SELECT
    userid,
    ARRAY_AGG(userid) OVER (
        PARTITION BY userid > 100
    ) arr,
    AVG(userid) OVER (
        PARTITION BY userid > 100
    ) avg
FROM users;

我会得到这个:

 userid |                            arr                            |         avg          
--------+-----------------------------------------------------------+----------------------
     46 | 46,23,69,92                                             |  57.5000000000000000
     23 | 46,23,69,92                                             |  57.5000000000000000
     69 | 46,23,69,92                                             |  57.5000000000000000
     92 | 46,23,69,92                                             |  57.5000000000000000
    552 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 629.2142857142857143
    ... | ...                                                       | ...
    529 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 629.2142857142857143

一切都好,但是如果相反,对于 100 的用户 ID 中:

SELECT
    userid,
    CASE WHEN userid > 100
    THEN ARRAY_AGG(userid) OVER (
        PARTITION BY userid > 100
    )
    ELSE ARRAY_AGG(userid) OVER (
        PARTITION BY userid -- OR userid > 100
        -- PARTITION BY userid > 100 OR CURRENT_ROW
        -- PARTITION BY userid > 100 OR userid = LAG(userid, 0) OVER ()
    )
    END arr
    CASE WHEN userid > 100
    THEN AVG(userid) OVER (
        PARTITION BY userid > 100
    )
    ELSE AVG(userid) OVER (
        PARTITION BY userid -- OR userid > 100
        -- PARTITION BY userid > 100 OR CURRENT_ROW
        -- PARTITION BY userid > 100 OR userid = LAG(userid, 0) OVER ()
    )
    END avg
FROM users;

上面所有注释的代码都是我一直在做的各种尝试。 我所拥有的最好的要么只是没有大于 100 的用户 ID,要么是所有用户 ID:

 userid |                            arr                            |         avg          
--------+-----------------------------------------------------------+----------------------
     23 | 23                                                      |  23.0000000000000000
     46 | 46                                                      |  46.0000000000000000
     69 | 69                                                      |  69.0000000000000000
     92 | 92                                                      |  92.0000000000000000
    552 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 629.2142857142857143
    ... | ...                                                       | ...
    529 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 629.2142857142857143

有什么方法可以做我正在寻找的东西吗?我也尽量不使用 CTE,因为实际代码中的技术债务太多,仅使用 WITH 来适应它需要相当长的时间。

要清楚,这是预期的结果:

 userid |                             arr                              |         avg
--------+--------------------------------------------------------------|----------------------
     23 | 23,552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 588.6000000000000000
     46 | 46,552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 590.1333333333333334
     69 | 69,552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 591.6666666666666667
     92 | 92,552,506,575,621,644,667,690,759,713,782,828,460,483,529 | 593.2000000000000000
    552 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529    | 629.2142857142857143
    ... | ...                                                          | ...
    529 | 552,506,575,621,644,667,690,759,713,782,828,460,483,529    | 629.2142857142857143

这是我一直在寻找的潜在未来内容的参考:nested window functions(但目前尚未实现,截至 Postgresql-11)

编辑:最后但同样重要的是,条件是占位符!它可能与用户 ID 相关,也可能不相关,这里只是为了举例,它可能是

CUME_DIST() OVER (
    PARTITION BY x -- OR CURRENT_USERID
)

【问题讨论】:

【参考方案1】:

这回答了问题的原始版本。

你似乎想要:

select (case when userid < 100
             then array_cat(array[userid],
                            array_agg(userid) filter (where userid > 100) over ()
             else array_agg(userid) filter (where userid > 100) over ()
        end)

【讨论】:

虽然我确实使用了 array_agg 作为示例,但只是为了方便显示示例。我要做的是 CUME_DIST () OVER (PARTITION BY condition OR userid ORDER BY sortorder) @BusyBeingDelicious 。 . .只能回答您实际提出的问题。如果您有不同的问题,请将其作为问题提出,并附上适当的解释、示例数据和期望的结果。 其实完全一样,只是不是数组操作,我用数组是因为比avg或cume_dist更容易看出发生了什么,而且标题也足够描述性。我将更新问题以包括 avg 也

以上是关于如何在 Postgresql 窗口函数的 PARTITION BY 中包含当前行的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PostgreSQL 8.3 中执行窗口函数

如何在 Postgresql 窗口函数的 PARTITION BY 中包含当前行

在 django ORM 中使用 postgresql 窗口函数的干净方法?

如何忽略 PostgreSQL 窗口函数中的空值?或返回列中的下一个非空值

在窗口函数 postgresql 中选择条件

PostgreSQL 如何查找并删除重复数据