在 Postgres 视图中折叠联合的结果

Posted

技术标签:

【中文标题】在 Postgres 视图中折叠联合的结果【英文标题】:Collapsing Results of a Union in a Postgres View 【发布时间】:2013-02-22 18:59:52 【问题描述】:

基本问题

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

我们需要的逻辑是:

    一个表中的数据优先于另一个表 具有相同key(多个字段的组合)的后续行会覆盖之前的行。 状态为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。

使用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 用于删除工作,并且更新的更改会覆盖以前的更改或官方数据?

【问题讨论】:

They each have identical tables and columns .. 所以我假设两个表中的列都应该命名为action_date?另外:Postgres 版本?如果我没记错的话,示例输出的最后一行应该以 o 开头。 很好,对两个帐户都适用。修复并添加了 Postgres 版本。 【参考方案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

一步一步

    在 CTE 中使用简单快速的 UNION ALL 从两个表中获取所有行 排除行,其中同一行(widget_id,variation_id)存在NOT EXISTS 排除带有status = 'D' 的行。 ORDER BY 并从widget_id, variation_id 合成密钥。

要点

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

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

status 列应该定义为NOT NULL,这样可以简化 WHERE 条件。

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

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

【讨论】:

哇,谢谢!我马上就要离开办公室了,但会在星期一进行测试。

以上是关于在 Postgres 视图中折叠联合的结果的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Postgres 中进行循环选择联合

使用 Django 从 Postgres 导出 JSON 时结果不一致

具有行展开和折叠选项的表格视图的最佳方法

使用 django 表单提交传递变量

使用联合查询将 bigquery 表与谷歌云 postgres 表合并

在Express中作为POST请求的结果呈现视图