Thread: Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
SELECT t.* ,FIRST_value(v1)OVER(PARTITION BY gid ORDER BY v2) fv ,LAST_value(v1)OVER(PARTITION BY gid ORDER BY v2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv FROM( VALUES (1, 'b', 3),(1, 'd', 1),(1, 'a', 2) ,(2, 'x', 7),(2, 'y', 9),(2, 'z', 8),(2, 'v', 9)) t (gid, v1, v2);
gid | v1 | v2 | fv | lv |
---|
1 | d | 1 | d | b |
1 | a | 2 | d | b |
1 | b | 3 | d | b |
2 | x | 7 | x | v |
2 | z | 8 | x | v |
2 | y | 9 | x | v |
2 | v | 9 | x | v |
but, given those values are repeating - why can't I simply use this functions as regular aggregates?
Am I missing some workaround here?
WITH t(gid, v1, v2) AS(
SELECT 1, 'b', 3 FROM dual UNION ALL
SELECT 1, 'd', 1 FROM dual UNION ALL
SELECT 1, 'a', 2 FROM dual UNION ALL
SELECT 2, 'x', 7 FROM dual UNION ALL
SELECT 2, 'y', 9 FROM dual UNION ALL
SELECT 2, 'z', 8 FROM dual UNION ALL
SELECT 2, 'v', 9 FROM dual --UNION ALL
)
SELECT t.*
,MIN(v1)KEEP(DENSE_RANK FIRST ORDER BY v2) OVER(PARTITION BY gid) lkp_first
,MIN(v1)KEEP(DENSE_RANK LAST ORDER BY v2) OVER(PARTITION BY gid) lkp_lastMin
,MAX(v1)KEEP(DENSE_RANK LAST ORDER BY v2) OVER(PARTITION BY gid) lkp_lastMax
FROM t;
GID | V1 | V2 | LKP_FIRST | LKP_LASTMIN | LKP_LASTMAX |
---|
1 | a | 2 | d | b | b |
1 | d | 1 | d | b | b |
1 | b | 3 | d | b | b |
2 | z | 8 | x | v | y |
2 | y | 9 | x | v | y |
2 | v | 9 | x | v | y |
2 | x | 7 | x | v | y |
WITH t(gid, v1, v2) AS(
SELECT 1, 'b', 3 FROM dual UNION ALL
SELECT 1, 'd', 1 FROM dual UNION ALL
SELECT 1, 'a', 2 FROM dual UNION ALL
SELECT 2, 'x', 7 FROM dual UNION ALL
SELECT 2, 'y', 9 FROM dual UNION ALL
SELECT 2, 'z', 8 FROM dual UNION ALL
SELECT 2, 'v', 9 FROM dual --UNION ALL
)
SELECT t.gid
,MIN(v1)KEEP(DENSE_RANK FIRST ORDER BY v2)lkp_first
,MIN(v1)KEEP(DENSE_RANK LAST ORDER BY v2)lkp_lastMin
,MAX(v1)KEEP(DENSE_RANK LAST ORDER BY v2)lkp_lastMax
FROM t
GROUP BY t.gid;
GID | LKP_FIRST | LKP_LASTMIN | LKP_LASTMAX |
---|
1 | d | b | b |
2 | x | v | y |
Re: Enhancement request: enable FIRST/LAST_value() also as a regular aggregate (not only as windowing function)
matshyeq <matshyeq@gmail.com> writes: > I'd like to throw here an enhancement proposal to discuss/consider. > The FIRST/LAST_value() > functions offer powerful lookup capabilities, ... > but, given those values are repeating - why can't I simply use this > functions as regular aggregates? > Or can I? It doesn't seem to be possible while I find this use case > actually more common than in windowing context… The reason the SQL standard doesn't allow that usage, or similar ones for other window functions, is that the function results are undefined unless you specify a particular ordering of the inputs ... which is exactly what the window syntax provides. Yeah, in most cases there are other ways where you could get the same result, but all of them require extra syntax too. So you might as well use window syntax and be standards-conforming. > Anyway, Oracle is an example where both contexts are possible Oracle is not exactly a shining example of either SQL spec compliance or well-thought-out features ... regards, tom lane
Re: Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
matshyeq <matshyeq@gmail.com> writes:
> I'd like to throw here an enhancement proposal to discuss/consider.
> The FIRST/LAST_value()
> functions offer powerful lookup capabilities, ...
> but, given those values are repeating - why can't I simply use this
> functions as regular aggregates?
> Or can I? It doesn't seem to be possible while I find this use case
> actually more common than in windowing context…
The reason the SQL standard doesn't allow that usage, or similar ones
for other window functions, is that the function results are undefined
unless you specify a particular ordering of the inputs ... which is
exactly what the window syntax provides.
Yeah, in most cases there are other ways where you could get the same
result, but all of them require extra syntax too. So you might as
well use window syntax and be standards-conforming.
> Anyway, Oracle is an example where both contexts are possible
Oracle is not exactly a shining example of either SQL spec compliance
or well-thought-out features ...
regards, tom lane
Re: Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
I'd like to throw here an enhancement proposal to discuss/consider.The FIRST/LAST_value() functions offer powerful lookup capabilities, eg.hereSELECT t.* ,FIRST_value(v1)OVER(PARTITION BY gid ORDER BY v2) fv ,LAST_value(v1)OVER(PARTITION BY gid ORDER BY v2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv FROM( VALUES (1, 'b', 3),(1, 'd', 1),(1, 'a', 2) ,(2, 'x', 7),(2, 'y', 9),(2, 'z', 8),(2, 'v', 9)) t (gid, v1, v2);
gid v1 v2 fv lv
1 d 1 d b 1 a 2 d b 1 b 3 d b 2 x 7 x v 2 z 8 x v 2 y 9 x v 2 v 9 x v
but, given those values are repeating - why can't I simply use this functions as regular aggregates?Or can I? It doesn't seem to be possible while I find this use case actually more common than in windowing context…
Am I missing some workaround here?
Re: Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
On Tue, Jul 24, 2018 at 4:16 PM, matshyeq <matshyeq@gmail.com> wrote:I'd like to throw here an enhancement proposal to discuss/consider.The FIRST/LAST_value() functions offer powerful lookup capabilities, eg.hereSELECT t.* ,FIRST_value(v1)OVER(PARTITION BY gid ORDER BY v2) fv ,LAST_value(v1)OVER(PARTITION BY gid ORDER BY v2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv FROM( VALUES (1, 'b', 3),(1, 'd', 1),(1, 'a', 2) ,(2, 'x', 7),(2, 'y', 9),(2, 'z', 8),(2, 'v', 9)) t (gid, v1, v2);
gid v1 v2 fv lv
1 d 1 d b 1 a 2 d b 1 b 3 d b 2 x 7 x v 2 z 8 x v 2 y 9 x v 2 v 9 x v
but, given those values are repeating - why can't I simply use this functions as regular aggregates?Or can I? It doesn't seem to be possible while I find this use case actually more common than in windowing context…
Am I missing some workaround here?Why not just define a custom aggregate function that does whatever you need? I don't think it would be too hard. e.g. for something like LAST_VALUE() just make the transition type equal to the output type and save the last value you've seen thus far as the transition value.--
Re: Enhancement request: enable FIRST/LAST_value() also as a regular aggregate (not only as windowing function)
matshyeq <matshyeq@gmail.com> writes: > Can I make custom aggregate function that accepts WITHIN GROUP syntax? It's possible, but it would have to be written in C, I think; see src/backend/utils/adt/orderedsetaggs.c for prototype code. You'll find it a lot easier to write a plain aggregate function and control its input ordering with "last(foo order by foo)". regards, tom lane
Re: Enhancement request: enable FIRST/LAST_value() also as a regularaggregate (not only as windowing function)
On 26/07/18 20:31, Tom Lane wrote: > matshyeq <matshyeq@gmail.com> writes: >> Can I make custom aggregate function that accepts WITHIN GROUP syntax? > > It's possible, but it would have to be written in C, I think; see > src/backend/utils/adt/orderedsetaggs.c for prototype code. You'll > find it a lot easier to write a plain aggregate function and control > its input ordering with "last(foo order by foo)". We have examples on the wiki. https://wiki.postgresql.org/wiki/First/last_(aggregate) -- Vik Fearing +33 6 46 75 15 36 http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support