如何使用pgSql

时间:2019-04-05 23:19:57

标签: sql postgresql

我需要将RESULT列放入同一行的SOMETHING和前一行的SOMETHING列之和(如果它是负数),例如B3 = A3 + MIN(0,B2)。

我尝试使用窗口函数,但无济于事,它只是显示出它不是预期的结果。 (month_year是DATE字段)

group   month_year  something   result
a         jan/19        -2      -2
a         fev/19        -4      -6
a         mar/19        -6      -12
a         abr/19        60      48
a         mai/19        -2      46
a         jun/19        9       55
a         jul/19        11      66
b         jan/19        100     100
b         fev/19        -200    -100
b         mar/19        300     200
b         abr/19        -50     150
b         mai/19        30      180
b         jun/19        -88     92
b         jul/19        -86     6

预期结果:

enter image description here

这是我要寻找的结果,如果还有其他方法可以实现我的全部追求。你能帮忙吗?

2 个答案:

答案 0 :(得分:1)

使用user-defined aggregate

实时测试:http://sqlfiddle.com/#!17/03ee7/1

DDL

CREATE TABLE t
    (grop varchar(1), month_year text, something int)
;

INSERT INTO t
    (grop, month_year, something)
VALUES
    ('a', '201901', -2),
    ('a', '201902', -4),
    ('a', '201903', -6),
    ('a', '201904', 60),
    ('a', '201905', -2),
    ('a', '201906', 9),
    ('a', '201907', 11),
    ('b', '201901', 100),
    ('b', '201902', -200),
    ('b', '201903', 300),
    ('b', '201904', -50),
    ('b', '201905', 30),
    ('b', '201906', -88),
    ('b', '201907', -86)
;

用户定义的聚合

create or replace function negative_accum(_accumulated_b numeric, _current_b numeric)
returns numeric as
$$
    select case when _accumulated_b < 0 then
        _accumulated_b + _current_b
    else
        _current_b
    end
$$ language 'sql';

create aggregate negative_summer(numeric)
(
    sfunc = negative_accum,
    stype = numeric,
    initcond = 0
);

select  
    *, 
  negative_summer(something) over (order by grop, month_year) as result
from t

第一个参数(_accumulated_b)保存该列的累加值。第二个参数(_current_b)保存当前行的列的值。

输出:

enter image description here

关于您的伪代码B3 = A3 + MIN(0, B2)

我使用了以下典型代码:

select case when _accumulated_b < 0 then
    _accumulated_b + _current_b
else
    _current_b
end

在Postgres中习惯上可以这样写:

select _current_b + least(_accumulated_b, 0)

实时测试:http://sqlfiddle.com/#!17/70fa8/1

create or replace function negative_accum(_accumulated_b numeric, _current_b numeric)
returns numeric as
$$
    select _current_b + least(_accumulated_b, 0) 
$$ language 'sql';

您还可以将其他语言与累加器功能配合使用,例如plpgsql。请注意,http://sqlfiddle.com中不支持plpgsql(或者可能是$$引号)。因此,没有实时测试链接,尽管它可以在您的计算机上运行:

create or replace function negative_accum(_accumulated_b numeric, _current_b numeric)
returns numeric as
$$begin
    return _current_b + least(_accumulated_b, 0);
end$$ language 'plpgsql';

更新

我错过了partition by,这是一个示例数据(将11更改为-11),如果没有partition by和带有partition by,则会产生不同的结果:

实时测试:http://sqlfiddle.com/#!17/87795/4

INSERT INTO t
    (grop, month_year, something)
VALUES
    ('a', '201901', -2),
    ('a', '201902', -4),
    ('a', '201903', -6),
    ('a', '201904', 60),
    ('a', '201905', -2),
    ('a', '201906', 9),
    ('a', '201907', -11), -- changed this from 11 to -11
    ('b', '201901', 100),
    ('b', '201902', -200),
    ('b', '201903', 300),
    ('b', '201904', -50),
    ('b', '201905', 30),
    ('b', '201906', -88),
    ('b', '201907', -86)
;

输出:

| grop | month_year | something | result_wrong | result |
|------|------------|-----------|--------------|--------|
|    a |     201901 |        -2 |           -2 |     -2 |
|    a |     201902 |        -4 |           -6 |     -6 |
|    a |     201903 |        -6 |          -12 |    -12 |
|    a |     201904 |        60 |           48 |     48 |
|    a |     201905 |        -2 |           -2 |     -2 |
|    a |     201906 |         9 |            7 |      7 |
|    a |     201907 |       -11 |          -11 |    -11 |
|    b |     201901 |       100 |           89 |    100 |
|    b |     201902 |      -200 |         -200 |   -200 |
|    b |     201903 |       300 |          100 |    100 |
|    b |     201904 |       -50 |          -50 |    -50 |
|    b |     201905 |        30 |          -20 |    -20 |
|    b |     201906 |       -88 |         -108 |   -108 |
|    b |     201907 |       -86 |         -194 |   -194 |

答案 1 :(得分:0)

您可能在使用window函数时遇到了问题,因为您需要订购一些东西,而且month_year列不会自然排序。请参见此SQL小提琴,在该列中将列替换为类似日期(可以正确排序)的内容。

http://sqlfiddle.com/#!18/7a304/1/0

CREATE TABLE t
    ([grop] varchar(1), [month_year] varchar(6), [something] int, [result] int)

INSERT INTO t
    ([grop], [month_year], [something], [result])
VALUES
    ('a', '201901', -2, -2),
    ('a', '201902', -4, -6),
    ('a', '201903', -6, -12),
    ('a', '201904', 60, 48),
    ('a', '201905', -2, -2),
    ('a', '201906', 9, 7),
    ('a', '201907', 11, 11),
    ('b', '201901', 100, 100),
    ('b', '201902', -200, -200),
    ('b', '201903', 300, 100),
    ('b', '201904', -50, -50),
    ('b', '201905', 30, -20),
    ('b', '201906', -88, -108),
    ('b', '201907', -86, -194)

select
  grop, month_year, something, result,
  sum(something) over (partition by grop order by grop, month_year) as rtot
from
  t

| grop | month_year | something | result | rtot |
|------|------------|-----------|--------|------|
|    a |     201901 |        -2 |     -2 |   -2 |
|    a |     201902 |        -4 |     -6 |   -6 |
|    a |     201903 |        -6 |    -12 |  -12 |
|    a |     201904 |        60 |     48 |   48 |
|    a |     201905 |        -2 |     -2 |   46 |
|    a |     201906 |         9 |      7 |   55 |
|    a |     201907 |        11 |     11 |   66 |
|    b |     201901 |       100 |    100 |  100 |
|    b |     201902 |      -200 |   -200 | -100 |
|    b |     201903 |       300 |    100 |  200 |
|    b |     201904 |       -50 |    -50 |  150 |
|    b |     201905 |        30 |    -20 |  180 |
|    b |     201906 |       -88 |   -108 |   92 |
|    b |     201907 |       -86 |   -194 |    6 |

问题的另一部分是当您获得正数时重置运行总计。我不确定是否可以在SQL中做到这一点,而无需放入存储的proc中,但是也许有一个更清晰的例子,有经验的人会加入。