将表结果合并到列中

时间:2017-05-17 21:32:35

标签: sql postgresql

(这是Merge table results into columns (pivot/crosstab?))的后续问题

我有~30个桌子,其中包含#34;流式传输"来自外部系统的数据。我试图弄清楚如何将收集的数据合并到一个查询结果中。

让我们来描述表格布局:

CREATE TABLE IF NOT EXISTS table1 (
    id1             INT NOT NULL,
    id2             TEXT NOT NULL,
    update_time     TIMESTAMP(6) NOT NULL,
    val             NUMERIC NULL,
PRIMARY KEY (id1, id2, update_time)
);

CREATE TABLE IF NOT EXISTS table2 (
    id1             INT NOT NULL,
    id2             TEXT NOT NULL,
    update_time     TIMESTAMP(6) NOT NULL,
    val             INT NULL,
    PRIMARY KEY (id1, id2, update_time)
);

--...tableN(


INSERT INTO table1(id1, id2, update_time, val) VALUES (1, 'ident 1', '2004-10-19 09:00:00', 1.23);
INSERT INTO table1(id1, id2, update_time, val) VALUES (1, 'ident 1', '2004-10-19 10:05:00', 1.25);

INSERT INTO table2(id1, id2, update_time, val) VALUES (1, 'ident 1', '2004-10-19 10:03:00', 23);
INSERT INTO table2(id1, id2, update_time, val) VALUES (1, 'ident 1', '2004-10-19 10:03:30', null);
INSERT INTO table2(id1, id2, update_time, val) VALUES (1, 'ident 1', '2004-10-19 10:05:00', 42);

是否可以在特定时间组合所有"已知数据"来自单个查询中的所有表?类似的东西:

SELECT update_time, t1_val, t2_val
FROM combined_output
WHERE start_time = '2004-10-19 08:00:00'
AND end_time = '2004-10-19 12:00:00'

哪会得到结果:

time                       t1_val    t2_val
'2004-10-19 09:00:00'      1.23      null
'2004-10-19 10:03:00'      1.23      23
'2004-10-19 10:03:30'      1.23      null
'2004-10-19 10:05:00'      1.25      42

一点解释:

在09:00:00,我们知道table1的值为1.23。 table2中没有值,因此该值应该为null。

在10:03:00,table2增加了23。 table1中的值1.23仍然是table1中的最后一个已知值,因此它仍应存在于输出中。

10:03:30如上。

10:05:00 table1和table2都获得了新值,但查询只返回输出中的一行,包含t1_val和t2_val中的两个新值。

在请求的时间范围之前过滤掉可能的值实际上并不重要。如果table2的值设置为08:59:00,如果该值在示例的第一行的t2_val中显示,即使它不是最佳的,也没有坏处。

(请注意,我有大约30个表来组合数据,因此寻找可以扩展到许多表的解决方案。无法更改表格布局。不需要高性能。)

3 个答案:

答案 0 :(得分:1)

我建议创建一个结合了所有数据的视图,然后根据需要查询视图。

创建视图:

create view combined_output as select * from table1 union all 
                               select * from table2 union all 
                               ...
                               select * from tableN;

运行查询:

SELECT update_time, t1_val, t2_val
FROM combined_output
WHERE update_time between '2004-10-19 08:00:00' and '2004-10-19 12:00:00'

警告:我没有尝试过这些。

答案 1 :(得分:0)

如果表格与外键相关联,则可以使用连接语句来完成 从你的表中看起来没有FK所以使用联盟。但是,这将为您提供大量数据。

答案 2 :(得分:0)

我找到了一个将函数与select结合起来的解决方案。

首先,我创建一个函数,返回特定时间的已知值:

DROP FUNCTION last_known_values(timestamp without time zone,integer,text);
CREATE OR REPLACE FUNCTION public.last_known_values(
    IN time_to_check timestamp without time zone,
    IN id1 integer,
    IN id2 text)
  RETURNS TABLE(checked_time timestamp without time zone, id1 integer, id2 text, t1_val numeric, t2_val int) AS
$BODY$

SELECT time_to_check AS time, id1, id2,
(
  SELECT table1.val AS t1_val from table1
  WHERE $1 >= table1.update_time
  AND table1.id1 = $2
  AND table1.id2 = $3
  ORDER BY table1.update_time DESC
  LIMIT 1
),
(
  SELECT table2.val AS t2_val from table2
  WHERE $1 >= table2.update_time
  AND table2.id1 = $2
  AND table2.id2 = $3
  ORDER BY table2.update_time DESC
  LIMIT 1
)

$BODY$
  LANGUAGE sql VOLATILE
  COST 100
  ROWS 1000;

然后我在任何时间戳范围内使用此函数,过滤所以只获取table1或table2(..tableN)中存在的时间戳:

SELECT last_known_values.* FROM (
    SELECT DISTINCT update_time 
    FROM (
        SELECT update_time 
        FROM table1
        WHERE update_time BETWEEN '2004-10-19 08:00:00' AND '2004-10-19 12:00:00'
        AND table1.id1 = 1
        AND table1.id2 = 'ident 1'

        UNION
        SELECT update_time 
        FROM table2
        WHERE update_time BETWEEN '2004-10-19 08:00:00' AND '2004-10-19 12:00:00'
        AND table2.id1 = 1
        AND table2.id2 = 'ident 1'
    ) t
    ORDER BY update_time ASC 
) times_to_fetch, last_known_values(times_to_fetch.update_time, 1, 'ident 1'::text);

给出结果:

"2004-10-19 09:00:00"    1    "ident 1"    1.23    (null)
"2004-10-19 10:03:00"    1    "ident 1"    1.23    23
"2004-10-19 10:03:30"    1    "ident 1"    1.23    (null)
"2004-10-19 10:05:00"    1    "ident 1"    1.25    42