admin

Greenplum中的滚动(移动)中值

sql

我想计算Greenplum 列的 滚动中位数 ,即如下:

|  x | rolling_median_x |
| -- + ---------------- |
|  4 |                4 |
|  1 |              2.5 |
|  3 |                3 |
|  2 |              2.5 |
|  1 |                2 |
|  6 |              2.5 |
|  9 |                3 |

x是一个整数,每行rolling_median_x显示x当前行和前一行的中位数。例如第三行rolling_median_x = median(4, 1, 3) = 3

到目前为止我发现的事情:

  • median函数不能用作框架窗口函数,即median(x) OVER(RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
  • 许多其他功能(例如percent_rank或)也是如此nth_value
  • 此版本的Greenplum不支持递归自联接

事实上,我找不到合适的文档,有关这些文档可以在Greenplum中用作框架窗口函数…

我正在使用Greenplum 4.3.4.0(基于Postgres 8.2.15),不幸的是更新不是一种选择。


阅读 217

收藏
2021-07-01

共1个答案

admin

一句话-维基百科的一篇引文:ORDER BY

ORDER BY是对结果集中的行进行排序的唯一方法。
没有此子句,关系数据库系统可以按任何顺序返回行。
如果需要排序,则必须在应用程序发送的SELECT语句中提供ORDER
BY。尽管某些数据库系统允许在子选择或视图定义中指定ORDER BY子句,但该显示不起作用。视图是逻辑关系表,
关系模型要求一个表是一组行,这意味着没有排序顺序。


由于您需要计算当前 行和前一行 的中位数,因此表中必须有一个附加行, 行定义 了行的顺序,
并且可以用来确定给定行之前的行和之后的行。
让我们说这样的一id列:

| id | x | rolling_median_x |
|----|---|------------------|
|  1 | 4 |                4 |
|  2 | 1 |              2.5 |
|  3 | 3 |                3 |
|  4 | 2 |              2.5 |
|  5 | 1 |                2 |
|  6 | 6 |              2.5 |
|  7 | 9 |                3 |

如果您不能使用解析函数,请尝试使用纯SQL。
本文介绍了使用SQL计算中位数的各种方法。
我认为,亨德森的中位数将最适合我们的需求:

SELECT CASE COUNT(*) % 2
       WHEN 0        -- even sized table
       THEN (P1.part_wgt + MIN(CASE WHEN P2.part_wgt > P1.part_wgt
                                  THEN P2.part_wgt
                                  ELSE NULL END))/2.0
       ELSE P1.part_wgt --odd sized table
       END AS median 
  FROM Parts AS P1, Parts AS P2
 GROUP BY P1.part_wgt
HAVING COUNT(CASE WHEN P1.part_wgt >= P2.part_wgt
                  THEN 1
                  ELSE NULL END)
       = (COUNT(*) + 1) / 2;

只需将每行的查询作为一个依赖子查询运行,一般的想法是这样的:

SELECT t.*, (
        SELECT .... Henderson's query FROM table x
        WHERE x.id <= t.id
        ......
       ) As our_median
FROM table t

您可以在此演示中找到示例实现

SELECT t.*, (
    SELECT CASE COUNT(*) % 2
           WHEN 0        -- even sized table
           THEN (P1.x + MIN(CASE WHEN P2.x > P1.x
                                      THEN P2.x
                                      ELSE NULL END))/2.0
           ELSE P1.x --odd sized table
           END AS median 
      FROM Table333 AS P1, Table333 AS P2
      WHERE p1.id <= t.id AND p2.id <= t.id
     GROUP BY P1.x
    HAVING COUNT(CASE WHEN P1.x >= P2.x
                      THEN 1
                      ELSE NULL END)
           = (COUNT(*) + 1) / 2
    ) as Our_median
FROM Table333 t;

| id | x | rolling_median_x | our_median |
|----|---|------------------|------------|
|  1 | 4 |                4 |          4 |
|  2 | 1 |              2.5 |        2.5 |
|  3 | 3 |                3 |          3 |
|  4 | 2 |              2.5 |        2.5 |
|  5 | 1 |                2 |          2 |
|  6 | 6 |              2.5 |        2.5 |
|  7 | 9 |                3 |          3 |

这个查询可能会很慢-这是您必须拥有古代版本的PostgreSQL所要付出的代价

2021-07-01