通过时间更新列

时间:2018-12-13 08:33:43

标签: sql postgresql

我有下表:

    id  Prod_id Year    Quartar Start_flag  End_flag    status
1   A001    2015    1   0   0   0
2   A001    2015    2   1   0   0
3   A001    2015    3   0   0   0
4   A001    2015    4   0   0   0
5   A001    2016    1   0   0   0
6   A001    2016    2   0   0   0
7   A001    2016    3   0   1   0
8   A001    2016    4   0   0   0
9   B002    2015    1   0   0   0
10  B002    2015    2   0   0   0
11  B002    2015    3   0   0   0
12  B002    2015    4   1   0   0
13  B002    2016    1   0   1   0
14  B002    2016    2   0   0   0
15  B002    2016    3   0   0   0
16  B002    2016    4   0   0   0
17  c003    2015    1   0   1   0
18  c003    2015    2   0   0   0
19  c003    2015    3   0   1   0
20  c003    2015    4   1   0   0
21  c003    2016    1   0   0   0
22  c003    2016    2   0   0   0
23  c003    2016    3   0   0   0
24  c003    2016    4   0   0   0

该表具有使用(Prod_id,Year,Quarter)的UNIQUE索引和在(Prod_id,Year,Quarter)上聚集的索引。 换句话说,表格是由 我的表具有以下内容:

  • 基于(Prod_id,Year,Quarter)的超过2000万个唯一行
  • 每个产品可能都有start_flag,但可能没有。
  • 如果产品具有开始标记,则在开始之前和之后都可能具有结束标记 标志,并且可能只有在开始标志之前才有结束标志。
  • 每个产品从2010年到2018年开始,每年有四个季度。

我想在从start_flag到start_flag之后的第一个end_flag之前的四分之一的时间内,将状态列= 1更新。如果没有结束标记,则更新将一直持续到选定产品的最后记录。

实施更新后,更新后的表应如下所示:

 id Prod_id Year    Quartar Start_flag  End_flag    status
1   A001    2015    1   0   0   0
2   A001    2015    2   1   0   1
3   A001    2015    3   0   0   1
4   A001    2015    4   0   0   1
5   A001    2016    1   0   0   1
6   A001    2016    2   0   0   1
7   A001    2016    3   0   1   0
8   A001    2016    4   0   0   0
9   B002    2015    1   0   0   0
10  B002    2015    2   0   0   0
11  B002    2015    3   0   0   0
12  B002    2015    4   1   0   1
13  B002    2016    1   0   1   0
14  B002    2016    2   0   0   0
15  B002    2016    3   0   0   0
16  B002    2016    4   0   0   0
17  c003    2015    1   0   1   0
18  c003    2015    2   0   0   0
19  c003    2015    3   0   1   0
20  c003    2015    4   1   0   1
21  c003    2016    1   0   0   1
22  c003    2016    2   0   0   1
23  c003    2016    3   0   0   1
24  c003    2016    4   0   0   1

我已经使用pl / pgsql使用游标植入了一个解决方案,但是对于我来说,使用游标的解决方案需要30多个小时才能更新此列。 有没有使用游标进行此更新的替代方法? 我正在使用PostgreSQL 10.6。 请咨询并谢谢您。

1 个答案:

答案 0 :(得分:0)

免责声明:我不太了解您的宿舍用例以及start_flag范围在一段时间内的工作方式。因此,以下解决方案无需关注时间部分。如果您可以稍微说明一下时间示例,那么我可以肯定,以下解决方案只需要稍作调整(例如PARTITION子句)即可为您工作。


demo:db<>fiddle

我的样本数据:

id   start_flag   end_flag
1    0            0
2    1            0
3    0            0
4    0            0
5    0            1
6    0            0
7    1            0
8    0            1
9    0            0
10   0            1
11   0            0
12   0            1
13   1            0
14   0            0
16   0            0

查询:

SELECT
    *,
    GREATEST(
         first_value(start_flag - end_flag) OVER (PARTITION BY sum ORDER BY id), 
         0
    ) as status
FROM (
    SELECT 
        *,
        SUM(start_flag + end_flag) OVER (ORDER BY id)
    FROM mytable
) s

结果:

id   start_flag   end_flag   status
1    0            0          0
2    1            0          1
3    0            0          1
4    0            0          1
5    0            1          0
6    0            0          0
7    1            0          1
8    0            1          0
9    0            0          0
10   0            1          0
11   0            0          0
12   0            1          0
13   1            0          1
14   0            0          1
16   0            0          1

说明:

使用window functions

  1. 每个潜在“状态更改”(SUMstart_flag)的累积end_flag。这将创建组。在每个组中都存在相同的状态。 (请参见小提琴中的SUM列)
  2. first_value窗口功能:对于每个组,检查状态更改是由start_flag(正)还是end_flag(负)完成的。
  3. GREATEST()将负值归一化为零。

编辑:添加prod_idyear/quarters

在所有用例中,总体来说都是安静的-仅使用窗口函数:

demo:db<>fiddle

  1. 我不是按照id进行排序,而是按照year, quarter进行排序
  2. 我没有使用整个数据集,而是使用了一个分区(在prod_id上)作为窗口函数:

扩展查询:

SELECT
    *, 
    GREATEST(
         first_value(start_flag - end_flag) OVER (PARTITION BY prod_id, sum ORDER BY year, quarter), 
         0
    ) as status
FROM (
    SELECT 
        *,
        SUM(start_flag + end_flag) OVER (PARTITION BY prod_id ORDER BY year, quarter)
    FROM mytable
) s