最快的查询技术取决于值的分布。 DISTINCT ON 将是 Postgres 中的一个简单解决方案,非常适合每个子表中每个 id 的几个值。但是从您的描述中猜测,我希望每个 id 有 许多 行,所以我建议使用 LATERAL 连接的解决方案。需要 Postgres 9.3+:
对于您已经不那么简单的案例还有一个复杂的问题:
值也可以是不同的类型
备选方案 1
将所有值转换为text。每种数据类型都可以转换为text。
基本查询
SELECT m.id, v.timestamp, 1 AS tbl, v.value -- simple int as table id
FROM master m
, LATERAL (
SELECT timestamp, value::text -- cast to text
FROM vtbl1
WHERE id = m.id -- lateral reference
ORDER BY timestamp DESC NULLS LAST
LIMIT 1
) v
UNION ALL
SELECT m.id, v.timestamp, 2 AS tbl, v.value -- ascending without gaps
FROM master m
, LATERAL (
SELECT timestamp, value::text
FROM vtbl2
WHERE id = m.id
ORDER BY timestamp DESC NULLS LAST
LIMIT 1
) v
UNION ALL
SELECT m.id, v.timestamp, 3 AS tbl, value
FROM ...
;
为了使这更快,您只需要在(id, timestamp) 上为每个子表建立一个索引。最好是这种形式(添加value 仅在您从中获得index-only scans 时才有用):
CREATE INDEX vtbl1_combo_idx ON vtbl1 (id, timestamp DESC NULLS LAST, value)
1a。聚合(伪交叉表)
要根据需要格式化 Postgres 9.3 或更早版本中的 CASE 表达式上的聚合函数(如 demonstrated by @sgeddes)或(更好)Postgres 9.4+ 中的新聚合 FILTER 子句:
SELECT id, timestamp
, max(value) FILTER (WHERE tbl = 1) AS val1
, max(value) FILTER (WHERE tbl = 2) AS val2
, ...
FROM ( <query frm above> ) t
GROUP BY 1, 2;
1b。交叉表
实际的交叉制表(在其他 RDBMS 中也称为“数据透视表”)应该快得多。您需要安装附加模块tablefunc,说明如下。
这里的特殊困难:我们有一个复合“行名”(id, timestamp),但该函数需要一个单个列作为行名。所以我们用row_number() 代替,但不要在结果中显示那个代理键:
SELECT id, timestamp, val1, val2, val3, ...
-- normally SELECT * is enough; explicit list to filter rn
FROM crosstab(
$$
SELECT row_number() OVER (ORDER BY id, timestamp DESC NULLS LAST) AS rn
, id, timestamp, tbl, value
FROM ( <query from above> ) t
ORDER BY 1
$$
, 'SELECT generate_series(1,3)' -- replace 3 with highest table nr.
) AS ct (
rn int, id int, timestamp date
, val1 text, val2 text, val3 text, ...);
密切相关:
相关基础知识:
备选方案 2
简单,但可能同样快并保留原始数据类型:
SELECT id, timestamp
, max(val1) AS val1, max(val2) AS val2, max(val3) AS val3, ...
FROM (
SELECT m.id, v.timestamp
, v.value AS val1, NULL::int AS val2, NULL::numeric AS val3, ...
-- list all values with actual data type
FROM master m
, LATERAL (
SELECT timestamp, value
FROM vtbl1
WHERE id = m.id
ORDER BY timestamp DESC NULLS LAST
LIMIT 1
) v
UNION ALL
SELECT m.id, v.timestamp
, NULL, v.value, NULL, ... -- column names & data types defined in first SELECT
FROM master m
, LATERAL (
SELECT timestamp, value
FROM vtbl2
WHERE id = m.id
ORDER BY timestamp DESC NULLS LAST
LIMIT 1
) v
UNION ALL
SELECT m.id, v.timestamp
, NULL, NULL, v.value, ...
FROM ...
) t
GROUP BY 1, 2
ORDER BY 1, 2;
除此之外:切勿使用基本类型名称或 reserved words(在标准 SQL 中)如 timestamp 作为标识符。