在Postgres视图中崩溃联盟的结果

时间:2013-02-22 18:59:53

标签: sql postgresql view union greatest-n-per-group

基本问题

在Postgres 9.2中是否有办法创建一个聚合来自两个表的数据的视图,其中涉及一些逻辑?

我们需要的逻辑是:

  1. 一个表中的数据优先于其他表
  2. 具有相同key的后续行(多个字段的组合)会覆盖以前的行。
  3. 状态为D的行会删除给定“密钥”的前任行。
  4. 更多细节和示例

    我在Postgres数据库中有2个模式。它们每个都有相同的表和列,但数据不同。一个用于官方数据,一个用于建议的更改。

    注意:我确信有更好的方法,但这是一个传统设置,无法更改。这是一个非常简化的,虚构的例子,但显示了我需要的情况和结果。

    所以我们有一个描述小部件的特征表。官方数据中只有一种数据类型(小部件将具有一种尺寸,一种颜色等)。

    提议的更改一旦获得批准,就会更改官方数据。对于给定类型的数据,可能存在多个挂起的更改。

    official架构

    CREATE TABLE characteristics (
        widget_id      integer NOT NULL,
        variation_id   integer NOT NULL,
        value          varchar(10),
        action_date    date,
        status         char(1)
    );
    

    official.characteristics中的示例数据:

    1,1,GI Joe,12/25/2012,C
    1,2,Green,12/25/2012,C
    1,3,M,12/25/2012,C
    1,4,Plastic,12/25/2012,C
    2,1,GI Joe,12/25/2012,C
    2,2,Green,12/25/2012,C
    2,3,L,12/25/2012,C
    2,4,Plastic,12/25/2012,C
    

    所以我们有2个小部件,一个是中等,绿色和塑料。一个是大的,绿色的和塑料的。

    proposed架构

    CREATE TABLE characteristics (
        widget_id      integer NOT NULL,
        variation_id   integer NOT NULL,
        value          varchar(10),
        action_date    date,
        status         char(1)
    );
    

    proposed.characteristics中的示例数据:

    1,2,Blue,2/22/2013,C
    1,4,Plastic,2/22/2013,D
    2,2,Purple,2/10/2013,C
    2,2,Green,2/22/2013,D
    2,3,XL,2/22/2013,C
    

    如果我们想看看所有提议的更改的结果是什么,我们可以查询两个表,更新的数据替换旧的,或D行删除任何先前的更改。

    SELECT
      'o' as src,
      lpad(widget_id::text,4,'0'::text) || '_' || lpad(variation_id::text,4,'0'::text) as key,
      *
    FROM
      proposed.characteristics
    ORDER BY
      key ASC,
      action_date::date ASC
    

    第二个查询是相同的,但在另一个表上,并且'p'作为src。

    使用PHP,我可以查询每个表,首先是官方的,然后是第二个建议的更改,并将数据放入一个以keywidget_id || '_' || variation_id)为键的数组中。任何新行都会覆盖旧行。如果statusD(用于删除),则删除带有密钥的行(尽管后续建议的更改可能会重新添加)。

    因此,对于上述数据,我们最终会得到:

    o,0001_0001,1,1,GI Joe,12/25/2012,C
    p,0001_0002,1,2,Blue,2/22/2013,C
    o,0001_0003,1,3,M,12/25/2012,C
    o,0002_0001,1,1,GI Joe,12/25/2012,C
    p,0002_0003,2,3,XL,2/22/2013,C
    o,0002_0004,2,4,Plastic,12/25/2012,C
    

    摘要

    有没有办法可以创建一个视图,我可以直接查询上面的结果?
    仍然有D用于删除工作,更新的更改会覆盖以前的更改或官方数据吗?

1 个答案:

答案 0 :(得分:1)

假设当前版本PostgreSQL 9.2缺乏信息。

执行此操作的一种方法是使用UNION ALL两个表CTE,并使用NOT EXISTS反半连接获取每个窗口小部件的最后一个有效版本:

CREATE VIEW my_viw AS
WITH x AS (
   SELECT 'o' as src, * FROM official.characteristics
   UNION ALL
   SELECT 'p' as src, * FROM proposed.characteristics
   )
SELECT lpad(widget_id::text, 4, '0')
       || '_' || lpad(variation_id::text, 4, '0') AS key, * -- pick columns
FROM   x
WHERE  NOT EXISTS (
   SELECT 1 FROM x y
   WHERE  y.widget_id = x.widget_id
   AND    y.variation_id = x.variation_id
   AND    y.action_date > x.action_date
   )
AND   (status <> 'D' OR status IS NULL)
ORDER  BY widget_id, variation_id

返回你概述的结果,除了我在评论中指出的错误。

->sqlfiddle

一步一步

  1. 使用CTE中的简单快速UNION ALL从两个表中获取所有行
  2. 排除行,其中NOT EXISTS
  3. 存在相同的行(widget_id,variation_id)
  4. 使用status = 'D'排除行。
  5. ORDER BY并从widget_id, variation_id合成密钥。
  6. 重点

    • 使用原始列widget_id, variation_id进行操作最有可能更快,并且只能在最终key中合成SELECT。代码更少,更容易索引。

    • 使用CTE,因为两个地方需要结果。

    • 应该定义statusNOT NULL,使WHERE条件更简单。

    • 两个表中的多列索引如下所示可能有助于提高性能。不确定它是否可以在UNION ALL之后使用。使用EXPLAIN ANALYZE进行测试以查看。

      CREATE INDEX characteristics_mult_idx
      ON official.characteristics (widget_id, variation_id, action_date DESC)