GT.*_*GT. 4 postgresql dynamic-sql
我在 64 位 Windows 7 上运行 PostgreSQL 9.3.5。
我的数据每季度到达,在多个表 ( table1, ..., tableN) 中,这些表在周期内通过基于关键标识符的跨表约束进行链接。在其他列中,每个表都具有随时间持续存在的标识符:pfi-持久特征标识符和ufi-通用特征标识符。
pfi每个表都是唯一的(非常罕见的是table1.pfi = table2.pfi.
ufi在所有表和所有时间都是唯一的。它不是行数据的散列,但您可以这样认为。
每个时期,在每个表中,一些新的pfi产生,一些旧pfi的退役。一些pfi改变属性。ufi跟踪给定(行)的任何属性的任何更改pfi,因此为它获取更改的(和新的)行table1只是一个问题:
-- 1st query
select a.*
into vm201512.property_d
from vm201512.property a
where not exists (select 1 from vm201412.property where ufi = a.ufi);
Run Code Online (Sandbox Code Playgroud)
这将选择pfi在至少一列中是新的 (new ) 或更改的所有行。
每个表的大约 96% 在各个方面都保持不变。因此,在分析跨周期变化时,我构建了一个仅包含更改数据和新数据的表。这将表大小从 ~3.5m 行减少到 ~225k 行:如果您随后对相对复杂的多边形和多个(空间和非空间)JOINs进行空间比较,那么这将大大减少。
该property表的列相对较少,因此我可以确定数据的哪些元素发生了变化,如下所示:
-- 2nd query
create table vm201512.property_d_changes as
select pfi,
case when a.view_pfi=b.view_pfi then 0::int else 1::INT end as view_pfi,
case when a.status=b.status then 0::int else 1::INT end as status,
case when a.property_type=b.property_type then 0::int else 1::INT end as property_type,
-- ... more columns
from vm201512.property_d a -- table created with first query
join vm201412.property b using (pfi);
Run Code Online (Sandbox Code Playgroud)
这给了我一个很好的表格,我可以在其中准确确定更改的(不是新的)行发生了什么变化。我可以弄清楚pfi123456 对其propnum及其status; 进行了更改。我可以弄清楚有多少pfi人对他们的view_pfi- 那种事情发生了变化。
其他几个表有 >50 列,这使得 case 语句变得笨拙(我意识到它只需要编码一次,但如果数据结构发生变化怎么办?)
有两个不同表new.table1, old.table1中的两行,其中new.table1.pfi = old.table1.pfi一列或多列不同,是否有简洁、优雅的 PostgreSQL 语句来找出更改的列?还是我被困住了CASE?
我意识到我可以编写一个动态函数来遍历给定表的所有列,并使用CASE语句构建查询。
您的评论需要首先解决:
数字数据几乎总是取 0(文本类型取 '')
这里的关键词是“几乎”。只要它不是“从不”(例如“从不!”),无论如何您都需要考虑 NULL。
没有测试风险
NULL=NULL,这会不恰当地返回 1
不,不会。任何与 NULL 相比的东西总是 NULL 甚至NULL=NULL。尝试一下。您需要了解 NULL 比较。
我想我只需要更改
sum(col1)为sum(col1::int)即可获取更改的行数col1。
如果您想计算 的每个案例a.col1 IS DISTINCT FROM b.col1,那么您需要首先使用NULL 安全比较。除此之外,你的表达会起作用。有多种选择,视情况而定:
您select a.* into vm201512 ...在第一个查询中使用。别。SELECT INTO ..气馁。CREATE TABLE AS ...在您的第二个查询中使用上级喜欢。
此外,Postgres 在tablefunc 模块中提供了枢轴功能,但这根本不是“枢轴”问题。这里没有任何旋转。
核心问题是由于不同的输入表而导致查询的动态特性。
假设没有 NULL 值。如果可以使用 NULL 值,请使用IS NOT DISTINCT FROM代替=。
在 Postgres 9.5 中测试。应该适用于 Postgres 9.1 或更高版本。
您可以像这样构建查询:
CREATE OR REPLACE FUNCTION f_build_query(_t1 regclass
, _t2 regclass
, _join_col text = 'pfi')
RETURNS text AS
$func$
SELECT format('SELECT %I, %s FROM %s a JOIN %s b USING (%1$I);'
, _join_col
, string_agg(format ('a.%1$I = b.%1$I AS %1$I', attname), ', ' ORDER BY attnum)
, _t1, _t2)
FROM pg_attribute
WHERE attrelid = _t1 -- compare all columns from 1st table
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
AND attname <> _join_col -- exclude "pfi"
$func$ LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)
称呼:
SELECT f_build_query('vm201512.property_d', 'vm201412.property');
Run Code Online (Sandbox Code Playgroud)
返回这样的查询(您可以依次执行):
SELECT pfi, a.a = b.a AS a, a."weird NaMe" = b."weird NaMe" AS "weird NaMe" -- more ...
FROM vm201512.property_d a JOIN vm201412.property b USING (pfi);
Run Code Online (Sandbox Code Playgroud)
结果:
pfi | a | b | weird NaMe
-----+---+---+------------
1 | t | f | t
2 | f | t | f
Run Code Online (Sandbox Code Playgroud)
适用于任意输入表,并安全地处理标识符。您可以根据需要提供符合或不符合架构的表名。
困难在于返回不同的行类型。SQL 要求在调用时知道返回类型。为避免困难,您可以改为返回一个简单的数组。您按列的原始顺序获得值,但不会像第一个查询那样获得列名:
CREATE OR REPLACE FUNCTION f_diff_matrix(_t1 regclass
, _t2 regclass
, _join_col text = 'pfi')
RETURNS TABLE (pfi int, change_matrix bool[]) AS -- Adapt data type of pfi to your needs!
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('SELECT %I, ARRAY[%s] FROM %s a JOIN %s b USING (%1$I)'
, _join_col
, string_agg(format ('a.%1$I = b.%1$I', attname), ', ' ORDER BY attnum)
, _t1, _t2)
FROM pg_attribute
WHERE attrelid = _t1 -- compare all columns from 1st table
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
AND attname <> _join_col -- exclude "pfi"
);
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
调用(注意区别!):
SELECT * FROM f_diff_matrix('vm201512.property_d', 'vm201412.property');
Run Code Online (Sandbox Code Playgroud)
结果:
pfi | change_matrix
-----+---------------
1 | {t,f,t} -- one element per column
2 | {f,t,f}
Run Code Online (Sandbox Code Playgroud)
你可能甚至做出相同的函数返回的各种表动态的结果集,但我怀疑它是值得的并发症:
如果您确实需要动态枢轴功能(不是在这种情况下):
| 归档时间: |
|
| 查看次数: |
1649 次 |
| 最近记录: |