【问题标题】:First and last value aggregate functions in postgresql that work correctly with NULL valuespostgresql 中的第一个和最后一个值聚合函数可以正确处理 NULL 值
【发布时间】:2013-12-03 08:07:45
【问题描述】:

我知道有聚合函数可以获取 postgresql 中行的最后一个值和第一个值

我的问题是,它们不能按我的需要工作。我可以使用一个 postgresql 向导的帮助。我正在使用 postgresql 9.2 - 如果该版本使提供解决方案更容易。

查询

select v.id, v.active, v.reg_no, p.install_date, p.remove_date 
from vehicle v 
    left join period p on (v.id = p.car_id) 
where v.id = 1 
order by v.id, p.install_date asc

返回 6 行:

id, active, reg_no, install_date, remove_date
1, TRUE, something, 2008-08-02 11:13:39, 2009-02-09 10:32:32
....
1, TRUE, something, 2010-08-15 21:16:40, 2012-08-25 07:44:30
1, TRUE, something, 2012-09-10 17:05:12, NULL

但是当我使用聚合查询时:

select max(id) as id, last(active) as active, first(install_date) as install_date, last(remove_date) as remove_date 
from (
    select v.id, v.active, v.reg_no, p.install_date, p.remove_date 
    from vehicle v 
      left join period p on (v.id = p.car_id) 
    where v.id = 1 
    order by v.id, p.install_date asc
) as bar 
group by id

然后我得到

id, active, install_date, remove_date
1, TRUE, 2008-08-02 11:13:39, 2012-08-25 07:44:30

不是

id, active, install_date, remove_date
1, TRUE, 2008-08-02 11:13:39, NULL

如我所料

如果最后一行的值为空而不是最后一个现有值,是否可以以某种方式更改聚合函数以产生 NULL?

EDIT1

Roman Pekar 为我的问题提供了alternative solution,但这不符合我的需求。原因是 - 我简化了原始查询。但我运行的查询更复杂。我意识到我的问题可能有其他解决方案 - 这就是为什么要更新帖子以包含原始的、更复杂的查询。即:

select partner_id, sum(active) as active, sum(installed) as installed, sum(removed) as removed 
from (
    select 
    pc.partner_id as partner_id, 
    v.id, 
    CASE WHEN v.active = TRUE THEN 1 ELSE 0 END as active, 
    CASE WHEN first(p.install_date) BETWEEN '2013-12-01' AND '2014-01-01' THEN 1 ELSE 0 END as installed,
    CASE WHEN last(p.remove_date) BETWEEN '2013-12-01' AND '2014-01-01' THEN 1 ELSE 0 END as removed 
    from vehicle v 
        left join period p on (v.id = p.car_id) 
        left join partner_clients pc on (pc.account_id = v.client_id) 
    group by pc.partner_id, v.id, v.active
) as foo group by partner_id

如您所见,我实际上需要获取几辆车的第一个和最后一个价值,而不是一个,最后汇总这些车辆的所有者对这些车辆的计数。

/EDIT1

【问题讨论】:

  • 您已链接到一个页面,该页面显示您如何自己实现该功能,如果您愿意,并指出 如何 NULLs 被忽略(通过参考STRICT)。你还想要什么?
  • 是的,这就是我对 postgresql 的了解程度——我知道如何将函数从 wiki 复制粘贴到 phpgadmin :P。这正是我所做的。如果您查看我发布的查询,您会看到我在那里使用这些功能。不过,我不知道的是他们实际上在做什么/意味着什么,以及如何让他们像我需要的那样工作。这就是我使用stackoverflow的原因:P。我想要的是有人告诉我如何更改这些功能,以便他们做我需要的事情。那么我需要用这个 STRICT 做什么来解决我的问题?
  • 我想我应该感谢 Damien :P。我在这些聚合函数中将 STRICT 切换为 CALLED ON NULL INPUT 并让它们工作。

标签: sql postgresql aggregate-functions


【解决方案1】:

您可以使用窗口函数lead() and lag() 来检查第一条和最后一条记录,例如:

select
    max(a.id) as id,
    max(a.first) as first,
    max(a.last) as last
from (
    select
         v.id,
         case when lag(v.id) over(order by v.id, p.install_date) is null then p.install_date end as first,
         case when lead(v.id) over(order by v.id, p.install_date) is null then p.remove_date end as last
    from vehicle v 
       left join period p on (v.id = p.car_id) 
    where v.id = 1 
) as a

sql fiddle demo

【讨论】:

  • 谢谢。这适用于我提出的情况 - 在一辆车的情况下。但我的问题是,我实际上在那里加入了另一个表,所以内部查询的结果实际上包含几辆车。我会更新原来的帖子。也许您可以提出更好的答案 - 或者您可以提供有关如何修改第一个和最后一个聚合函数的见解。
  • 修改了原帖。如果我将您的查询用作子查询,那么其他车辆安装和删除日期的所有值都是 NULL。
【解决方案2】:

感谢 Damien,我阅读了有关创建函数 (source) 的 postgresql 文档,并修改了从以下位置更改它的函数:

CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

到:

CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE CALLED ON NULL INPUT AS $$
        SELECT $2;
$$;

CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

它似乎解决了我的问题。

感谢阅读。

【讨论】:

    猜你喜欢
    • 2017-11-06
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多