2012-10-19 13 views
8

Ho due tabelle cronologia che tengono traccia delle modifiche nei valori del database, utilizzando un ID revisione per tenere traccia delle singole modifiche. per esempio.Unione di due tabelle di rilevamento versione durante il riempimento dei valori

Tabella 1:

rev | A | B 
================= 
1 | 100 | 'A' 
4 | 150 | 'A' 
7 | 100 | 'Z' 

Tabella 2:

rev | C | D 
================== 
1 | 200 | True 
5 | 0 | True 
8 | 0 | False 

L'obiettivo è quello di unire le due tabelle in:

rev | A | B | C | D 
=============================== 
1 | 100 | 'A' | 200 | True 
4 | 150 | 'A' | 200 | True 
5 | 150 | 'A' | 0 | True 
7 | 100 | 'Z' | 0 | True 
8 | 100 | 'Z' | 0 | False 

L'idea è che per un determinato revisione, prenderei i valori corrispondenti a quella revisione o la revisione più alta meno di essa.

La query SQL che viene in mente sarebbe qualcosa di simile ad attraversare congiungente i due tavoli con vincolo rev1 < rev2, quindi selezionando righe utilizzando una subquery dove rev1 = max (REV1) per ogni dato rev2 ; unione di questa query con la sua controparte scambiando rev2 e rev1; e infine filtrare i duplicati da dove rev1 = rev2.

Le domande sono:

  • C'è un nome per questo tipo di join?
  • Esiste un idioma per eseguire questo tipo di join in SQL, o sarebbe meglio farlo a livello di codice (che sarebbe sicuramente molto più semplice ed eventualmente più efficiente)?
+0

Cosa RDBMS? Alcuni hanno il supporto per questi tipi di operazioni, quindi (specialmente se lo spazio dati è grande) questo potrebbe effettivamente essere più efficiente nel database. –

+0

Quindi non vuoi la query ma solo la risposta a queste due domande? –

+0

Il database è PostgreSQL, anche se tecnicamente il lavoro dovrebbe essere indipendente dal DB (realisticamente questo non accadrà). E sì, sono solo interessato alle risposte alle domande, a meno che non ci sia una domanda molto più semplice a cui sto trascurando. –

risposta

2

SQL Fiddle

select 
    coalesce(t1.rev, t2.rev) rev, 
    coalesce(a, lag(a, 1) over(order by coalesce(t2.rev, t1.rev))) a, 
    coalesce(b, lag(b, 1) over(order by coalesce(t2.rev, t1.rev))) b, 
    coalesce(c, lag(c, 1) over(order by coalesce(t1.rev, t2.rev))) c, 
    coalesce(d, lag(d, 1) over(order by coalesce(t1.rev, t2.rev))) d 
from 
    t1 
    full join 
    t2 on t1.rev = t2.rev 
order by rev 
1

Ciò può essere ottenuto da sub interroga

SELECT ISNULL(Table1.rev,Table2.rev) AS rev 
,ISNULL(A,(SELECT TOP 1 A FROM Table1 AS T1 WHERE ISNULL(Table1.rev,Table2.rev) > T1.rev AND A IS NOT NULL ORDER BY rev DESC)) AS A 
,ISNULL(B,(SELECT TOP 1 B FROM Table1 AS T1 WHERE ISNULL(Table1.rev,Table2.rev) > T1.rev AND B IS NOT NULL ORDER BY rev DESC)) AS B 
,ISNULL(C,(SELECT TOP 1 C FROM Table2 AS T2 WHERE ISNULL(Table1.rev,Table2.rev) > T2.rev AND C IS NOT NULL ORDER BY rev DESC)) AS C 
,ISNULL(D,(SELECT TOP 1 D FROM Table2 AS T2 WHERE ISNULL(Table1.rev,Table2.rev) > T2.rev AND D IS NOT NULL ORDER BY rev DESC)) AS D 
FROM Table1 
FULL OUTER JOIN Table2 
ON Table1.rev = Table2.rev 
+0

'isnull' non è una sintassi postgresql valida –

0

Non c'è specifico tipo di join per gestire questo tipo di query. Devi farlo come una query complessa o programmaticamente. Di seguito è riportato un esempio di codice PL/PGSQL per questo problema, utilizzando i dati di esempio.

CREATE OR REPLACE FUNCTION getRev(OUT rev INT, OUT A INT, OUT B CHAR, OUT C INT, OUT D BOOL) RETURNS SETOF record STABLE AS 
$BODY$ 
DECLARE 
    c1 SCROLL CURSOR FOR SELECT * FROM Table1 ORDER BY rev; 
    c2 SCROLL CURSOR FOR SELECT * FROM Table2 ORDER BY rev; 
    r1 Table1%ROWTYPE; 
    r1c Table1%ROWTYPE; 
    r2 Table2%ROWTYPE; 
    r2c Table2%ROWTYPE; 
BEGIN 
    OPEN c1; 
    OPEN c2; 
    FETCH c1 INTO r1; 
    FETCH c2 INTO r2; 
    r1c := r1; 
    r2c := r2; 
    WHILE r1 IS NOT NULL AND r2 IS NOT NULL 
    LOOP 
    CASE 
    WHEN r1.rev = r2.rev THEN 
     rev := r1.rev; 
     A := r1.a; 
     B := r1.b; 
     C := r2.c; 
     D := r2.d; 
     FETCH c1 INTO r1c; 
     FETCH c2 INTO r2c; 
     CASE 
     WHEN r1c.rev = r2c.rev THEN 
     r1 := r1c; 
     r2 := r2c; 
     WHEN r1c.rev < r2c.rev THEN 
      r1 := r1c; 
     FETCH PRIOR FROM c2 INTO r2c; 
    ELSE 
      r2 := r2c; 
     FETCH PRIOR FROM c1 INTO r1c; 
     END CASE; 
    WHEN r1.rev < r2.rev THEN 
     WHILE r1c IS NOT NULL AND r1c.rev < r2.rev LOOP 
     r1 := r1c; 
     FETCH c1 INTO r1c; 
     END LOOP; 
     rev := r2.rev; 
     A := r1.a; 
     B := r1.b; 
     C := r2.c; 
     D := r2.d; 
     r1 := r1c; 
    ELSE 
     WHILE r2c IS NOT NULL AND r2c.rev < r1.rev LOOP 
     r2 := r2c; 
     FETCH c2 INTO r2c; 
     END LOOP; 
     rev := r1.rev; 
     A := r1.a; 
     B := r1.b; 
     C := r2.c; 
     D := r2.d; 
     r2 := r2c; 
    END CASE; 
    RETURN NEXT; 
    END LOOP; 
    CLOSE c1; 
    CLOSE c2; 
    RETURN; 
END 
$BODY$ 
LANGUAGE 'plpgsql'; 

Questo dovrebbe essere eseguito in O (lunghezza (Tabella1) + lunghezza (Tabella2)).

Nota la parte difficile nel "CASO QUANDO r1.rev = r2.rev": dobbiamo scegliere su quale tabella continuare la scansione per la successiva iterazione. Quello corretto è quello con il più piccolo valore di giri dopo il cursore, per ottenere tutti i numeri di giri disponibili in entrambe le tabelle. Potresti sicuramente ottenere prestazioni migliori codificandolo in C o C++.