2

基本问题

Postgres 9.2 中是否有一种方法可以创建一个视图来聚合来自两个表的数据,并涉及一些逻辑?

我们需要的逻辑是:

  1. 一个表中的数据优先于另一个表
  2. 具有相同key(多个字段的组合)的后续行会覆盖以前的行。
  3. 状态为D删除给定“键”的任何先前行的行。

更多细节和示例

我在 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。

key使用PHP,我可以查询每个表,首先是官方的,其次是提议的更改,然后将数据放入以(widget_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删除工作,并且更新的更改会覆盖以前的更改或官方数据?

4

1 回答 1

1

由于缺少信息,假设当前版本为 PostgreSQL 9.2。

一种方法是使用两个表中的一个CTE UNION ALL并为每个带有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. UNION ALL在 CTE 中简单快速地从两个表中获取所有行
  2. 排除行,其中存在相同(widget_id,variation_id)的后续行NOT EXISTS
  3. 用 排除行status = 'D'
  4. ORDER BY 并从中合成密钥widget_id, variation_id

要点

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

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

  • status应该定义列NOT NULL,这将使 WHERE 条件更简单。

  • 两个表上的如下多列索引可能有助于提高性能。不知道以后能不能用UNION ALL。测试EXPLAIN ANALYZE一下看看。

    CREATE INDEX characteristics_mult_idx
    ON official.characteristics (widget_id, variation_id, action_date DESC) 
    
于 2013-02-22T21:32:59.477 回答