在 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,我可以查询每个表,首先是官方,其次是提出更改,并将数据放入以key
(widget_id || '_' || variation_id
)为键的数组中。任何新行都会覆盖旧行。如果status
是D
(用于删除),则删除带有键的行(尽管后续提议的更改可能会重新添加它)。
所以对于上面的数据,我们最终会得到:
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 视图中折叠联合的结果的主要内容,如果未能解决你的问题,请参考以下文章
使用 Django 从 Postgres 导出 JSON 时结果不一致