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 < rev2交叉连接两个表,然后使用子查询选择行,其中rev1 = max(rev1)对于每个给定的rev2;将此查询与其对应的交换rev2rev1联合;最后从rev1 = rev2中过滤掉重复项。

问题是:

  • 这种类型的连接有名称吗?
  • 是否有在 SQL 中执行这种类型的连接的习惯用法,或者以编程方式执行它会更好(这肯定会更简单并且可能更有效)?
4

3 回答 3

2

SQL小提琴

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
于 2012-10-25T20:26:11.300 回答
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
于 2012-10-23T07:54:04.197 回答
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(length(Table1) + length(Table2)) 中运行。

请注意“CASE WHEN r1.rev = r2.rev”中的棘手部分:我们必须选择在哪个表上继续扫描以进行下一次迭代。正确的一个是光标后转速值最小的那个,以获取两个表中可用的所有转速。您当然可以通过用 C 或 C++ 对其进行编码来获得更好的性能。

于 2012-10-25T16:52:46.380 回答