Postgresql增量和条件过滤

时间:2013-04-17 15:17:34

标签: sql postgresql

我有这张桌子

 ID    value    user      stock
----|--------|---------|---------
  1 |   10   | mark    |  AAPL
  2 |   20   | rob     |  GOOG
  3 |   30   | mark    |  AAPL
  4 |  -40   | mark    |  AAPL
  5 |  -10   | rob     |  GOOG
  6 |   25   | mark    |  GOOG
  7 |    5   | mark    |  GOOG
  8 |   45   | mark    |  AAPL

我想构建一个返回下面显示的行的查询(可能不使用任何PGSQL函数)。它应该按顺序开始(ID ASC)求和"值"按用户,库存分组的列。如果临时总和为0,则将丢弃所有先前的行(针对该组)。

 id    value    user      stock
----|--------|---------|---------
  2 |   20   | rob     |  GOOG
  5 |  -10   | rob     |  GOOG
  6 |   25   | mark    |  GOOG
  7 |    5   | mark    |  GOOG
  8 |   45   | mark    |  AAPL

我认为应该使用OVER(PARTITION BY)和WINDOW函数 SELECT *, SUM(value) OVER w AS scm FROM "mytable" WINDOW w AS (PARTITION BY user,stock ORDER BY id ASC)

这将返回下一个表

 ID    value    user      stock    scm
----|--------|---------|---------|-------
  1 |   10   | mark    |  AAPL   | 10
  2 |   20   | rob     |  GOOG   | 20
  3 |   30   | mark    |  AAPL   | 40
  4 |  -40   | mark    |  AAPL   |  0
  5 |  -10   | rob     |  GOOG   | 10
  6 |   25   | mark    |  GOOG   | 25
  7 |    5   | mark    |  GOOG   | 30
  8 |   45   | mark    |  AAPL   | 45

所以这应该是一个很好的起点,因为它表明APPL对于mark是0(id = 4),对于那个组(AAPL,mark),我应该保留以下所有行。 规则是:对于每个组(stock,user),使用scm = 0

保留最后一行之后的所有行

2 个答案:

答案 0 :(得分:1)

SQL Fiddle

with s as (
    select *,
        count(scm = 0 or null) over w z
    from (
        select *,
            sum(value) over w as scm
        from mytable
        window w as (partition by "user", stock order by id asc)
    ) s
    window w as (partition by "user", stock order by id asc)
)
select *
from
    s
    inner join
    (
        select max(z) z, "user", stock
        from s
        group by "user", stock
    ) z using (z, "user", stock)
where scm > 0
order by s.user, s.stock, id

答案 1 :(得分:0)

以下我认为会让你想要的东西。基本上它将执行以下操作:

  1. 使用您需要的SQL语句计算累计总和。
  2. 计算每个(username, stock)组应显示的最小ID。
  3. 从原始SQL累积总和中进行选择,并筛选出低于最小ID的任何ID。
  4. WITH sums AS (
      SELECT id, value, username, stock, SUM(value) OVER w AS scm
        FROM "mytable"
      WINDOW w AS (PARTITION BY user,stock ORDER BY id ASC)),
    
    minimum_ids AS (
      SELECT username, stock, MAX(id) as minimum_id
        FROM sums
       WHERE scm <= 0
       GROUP BY username, stock)
    
    SELECT sums.id, sums.value, sums.username, sums.stock, sums.scm
      FROM sums
      LEFT JOIN minimum_ids
        ON (sums.username = minimum_ids.username
            AND sums.stock = minimum_ids.stock)
     WHERE (minimum_ids.minimum_id IS NULL OR sums.id > minimum_ids.minimum_id)
     ORDER BY id;