2017-05-17 3 views
1

(Dies ist eine Follow-up-Frage zu Merge table results into columns (pivot/crosstab?))Merge Tabelle führt in Spalt

Ich habe ~ 30 Tabellen, die „gestreamt“ Daten von einem externen System bekommt. Ich versuche herauszufinden, wie die gesammelten Daten zu einem einzigen Abfrageergebnis kombiniert werden können.

Lassen Sie uns das Tabellenlayout beschreiben:

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); 

Ist es möglich, alle „zu einem bestimmten Zeitpunkt bekannten Daten“ zu kombinieren aus allen Tabellen in einer einzigen Abfrage? Etwas wie:

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 

Eine kleine Erklärung:

Am 09.00.00 wir wussten, dass tabelle1 hatte einen Wert von 1,23

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' 

die das Ergebnis geben würde. In Tabelle 2 war kein Wert vorhanden, daher sollte der Wert null sein.

Um 10:03:00 Tabelle2 wurde 23 hinzugefügt. Der Wert 1.23 in table1 ist immer noch der letzte bekannte Wert aus table1, der in der Ausgabe noch vorhanden sein sollte.

10:03:30 wie oben.

10:05:00 Sowohl table1 als auch table2 haben neue Werte, aber die Abfrage gibt nur eine einzige Zeile in der Ausgabe zurück, die beide neuen Werte in t1_val und t2_val enthält.

Es ist eigentlich nicht kritisch, mögliche Werte vor dem angeforderten Zeitbereich wegzufiltern. Wenn table2 einen Wert von 08:59:00 hätte, wäre es kein Nachteil, wenn dieser Wert in t2_val in der ersten Zeile des Beispiels angezeigt wird, auch wenn er nicht optimal ist.

(Beachten Sie, dass ich ~ 30 Tabellen kombinierten Daten aus, so dass für eine Lösung, die zu vielen Tabellen erweitert werden kann. Ändern das Tabellenlayout ist nicht möglich. Hohe Leistung nicht erforderlich ist.)

+0

Sie können 'verwenden voll join' aber die Abfrage würde mit so vielen Tabellen umständlich aussehen. –

Antwort

0

Ich habe eine Lösung gefunden, die eine Funktion mit Auswahl kombiniert.

Zuerst schaffe ich eine Funktion, die die bekannten Werte für eine bestimmte Zeit zurückgibt:

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; 

Dann verwende ich diese Funktion mit jedem Zeitstempel-Bereich, so Filterung nur vorhanden Zeitstempel in Tabelle 1 oder Tabelle 2 (..tableN) geholt:

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); 

Gibt das Ergebnis:

"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 
1

I Ich empfehle Ihnen, eine Ansicht zu erstellen, in der alle Daten kombiniert werden, um die Ansicht nach Bedarf abfragen zu können.

Ansicht erstellen:

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

Run query:

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' 

Caveat: Ich habe irgendetwas davon nicht versucht.

+0

Wenn Sie eine Union verwenden, werden alle Werte in derselben Spalte platziert. Ich suche nach einer Möglichkeit, um die verschiedenen Tabellen in separate Spalten zu bekommen, aber mit den "letzten bekannten Werten" zu einer einzigen Zeile zusammengeführt. –

0

Wenn die Tabellen mit einem Fremdschlüssel verknüpft sind, könnte dies mit einer Join-Anweisung geschehen.
Von was es aussieht von Ihren Tabellen gibt es keine FK so verwenden Sie eine Union. Dies wird Ihnen jedoch viele Daten liefern.

+0

Wie oben erwähnt, wird eine Union die Daten aus den verschiedenen Tabellen in einer einzigen Spalte zusammenführen und nicht wie gewünscht trennen. –

Verwandte Themen