带有边界的SQLAlchemy sum函数

bnj*_*jmn 4 python postgresql sqlalchemy window-functions

sqlalchemy(postgresqlDB)中,我想创建一个有界和函数,因为缺少一个更好的术语.目标是在定义的范围内创建运行总计.

目前,我有一些非常适合计算没有边界的运行总计的东西.像这样的东西:

from sqlalchemy.sql import func

foos = (
    db.query(
        Foo.id,
        Foo.points,
        Foo.timestamp,
        func.sum(Foo.points).over(order_by=Foo.timestamp).label('running_total')
    )
    .filter(...)
    .all()
)
Run Code Online (Sandbox Code Playgroud)

但是,我希望能够将这个运行总数限制在一个特定的范围内,比如说[-100, 100].所以我们会得到这样的东西(见running_total):

{'timestamp': 1, 'points': 75, 'running_total': 75}
{'timestamp': 2, 'points': 50, 'running_total': 100}
{'timestamp': 3, 'points': -100, 'running_total': 0}
{'timestamp': 4, 'points': -50, 'running_total': -50}
{'timestamp': 5, 'points': -75, 'running_total': -100}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

poz*_*ozs 5

不幸的是,没有内置聚合可以帮助您通过窗口函数调用实现预期的输出.

您可以通过递归CTE逐个手动计算行来获得预期的输出:

with recursive t as (
  (select   *, points running_total
   from     foo
   order by timestamp
   limit    1)
  union all
  (select   foo.*, least(greatest(t.running_total + foo.points, -100), 100)
   from     foo, t
   where    foo.timestamp > t.timestamp
   order by foo.timestamp
   limit    1)
)
select timestamp,
       points,
       running_total
from   t;
Run Code Online (Sandbox Code Playgroud)

不幸的是,使用SQLAlchemy很难实现这一点.

您的另一个选择是,根据您的特定需求编写自定义聚合,例如:

create function bounded_add(int_state anyelement, next_value anyelement, next_min anyelement, next_max anyelement)
  returns anyelement
  immutable
  language sql
as $func$
  select least(greatest(int_state + next_value, next_min), next_max);
$func$;

create aggregate bounded_sum(next_value anyelement, next_min anyelement, next_max anyelement)
(
    sfunc    = bounded_add,
    stype    = anyelement,
    initcond = '0'
);
Run Code Online (Sandbox Code Playgroud)

有了这个,您只需要将呼叫替换sum为以下呼叫bounded_sum:

select timestamp,
       points,
       bounded_sum(points, -100.0, 100.0) over (order by timestamp) running_total
from   foo;
Run Code Online (Sandbox Code Playgroud)

后一种解决方案也可能更好地扩展.

http://rextester.com/LKCUK93113