2012-10-19 27 views
8

我有兩個歷史表跟蹤數據庫值的更改,使用版本ID跟蹤各個更改。例如在填寫值時合併兩個版本跟蹤表

表1:

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

表2:

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

我們的目標是將兩個表合併成:

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 

的想法是,對於一個給定的修訂版中,我會將與該修訂版相對應的值或最高版本修改爲小於它的值。

,想到的SQL查詢將是一個類似於十字約束REV1連接兩個表<轉2,然後使用子查詢,其中給出轉2REV1 = MAX(REV1)每個選擇行;將該查詢與其對應的交換rev2rev1;最後過濾掉rev1 = rev2的重複項。

的問題是:

  • 是否有這種類型的連接的名稱?
  • 在SQL中執行這種類型的連接有沒有一個習慣用法,還是以編程方式執行它會更好?(這肯定會更簡單和可能更有效)?
+0

RDBMS是什麼?有些支持這些類型的操作,所以(特別是在數據空間很大的情況下),這在數據庫中實際上可能更有效。 –

+0

所以你不想要查詢,而只是這兩個問題的答案? –

+0

數據庫是PostgreSQL,儘管技術上這項工作應該獨立於數據庫(實際上這不會發生)。是的,只是對問題的答案感興趣,除非我忽略了一個更簡單的查詢。 –

回答

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

這可以通過子可以實現查詢

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'是無效的postgresql語法 –

0

有沒有具體的連接類型來處理這一類的查詢。您必須以複雜查詢或編程方式執行此操作。以下是使用您的示例數據的PL/PGSQL代碼示例。

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

這應該運行在O(長度(表1)+長度(表2))。

注意「CASE WHEN r1.rev = r2.rev」中的棘手部分:我們必須選擇在哪個表上繼續下一次迭代的掃描。正確的是光標後的轉速值最小的那個值,以通過兩個表中可用的所有轉速值。通過在C或C++中編寫代碼,你當然可以獲得更好的性能。