在连接中避免多行

时间:2014-03-19 01:40:33

标签: sql oracle join

假设我有三个表,A,B和C:

表A:

C1  C2  Dt
-------------
1   2   8 pm
1   2   10 pm

表B:

C1  C2  Ind
-------------
1   2   123
1   2   456

表C:

C1  C2  C3  C4  Ind
-------------------
1   2   a   b   123
1   2   c   d   123
1   2   e   f   123
1   2   g   h   456

如您所见,表B和C具有匹配的索引,而A则没有。如何连接三个表,以便A的第一行(由'dt'列排序)仅匹配C中的行,哪个索引是B中的第一行(按Ind排序)?这同样适用于其他行。

我尝试过创建一个简单的连接:

SELECT *
FROM A JOIN B
       ON A.C1 = B.C1
       AND A.C2 = B.C2
JOIN C ON A.C1 = C.C1
       AND A.C2 = C.C2
       AND B.IND = C.IND

我知道这不起作用,因为A中的每一行都会匹配B中的所有行,然后匹配C中的所有行。换句话说,没有唯一的匹配。

我认为另一种方法是使用两种选择:

SELECT *
FROM B JOIN (
            SELECT C1, C2, C3, C4, Ind,
                   row_number() OVER (PARTITION BY C1, C2, ind ORDER BY C1, C2, ind) AS num_row
            FROM C
            ) table_c
        ON B.IND = table_c.IND
        AND B.C1 = table_c.C1
        AND B.C2 = table_c.C2
JOIN (
        SELECT C1, C2, DT, row_number() OVER (ORDER BY DT) AS num_row
        FROM A
        ) table_a
        ON table_a.num_row = table_c.num_row
        AND table_a.C1 = table_c.C1
        AND table_a.C2 = table_c.C2

但是那些表格非常大,而且我尝试过的每种方法都会使用多个选项而且非常慢。所以我想知道最好的方法是什么。

2 个答案:

答案 0 :(得分:0)

AB具有一对一的关系。因此,根据每个ID的顺序将它们加入到一个唯一的id中应该可以解决问题的第一部分。

create table newA as select rownum as uniq_id, A.* from A order by dt;
create table newB as select rownum as uniq_id, B.* from B order by ind;
select * from newA inner join newB on newA.uniq_id = newB.uniq_id;

然后使用新查询加入C

select * 
from 
  C 
inner join (select 
             newB.Ind as ind 
            from 
              newA 
              inner join newB on newA.uniq_id = newB.uniq_id) 
          as sub on C.ind = sub.ind

我确信这可以使用临时表或严格在sql中完成,但这取决于您的实现

答案 1 :(得分:0)

您可以在此处有效地使用ROW_NUMBER

WITH arn 
     AS (SELECT a.c1, 
                a.c2, 
                "Dt", 
                Row_number() 
                  over ( 
                    PARTITION BY a.c1, a.c2 
                    ORDER BY "Dt")rn 
         FROM   a), 
     brn 
     AS (SELECT b.c1, 
                b.c2, 
                b."Ind", 
                Row_number() 
                  over ( 
                    PARTITION BY b.c1, b.c2 
                    ORDER BY b."Ind") rn 
         FROM   b) 
SELECT * 
FROM   arn a 
       inner join brn b 
               ON a.c1 = b.c1 
                  AND a.c2 = b.c2 
                  AND a.rn = b.rn 
       Inner join c
               ON b.c1 = c.c1
                  AND b.c2 = c.c2
                  AND b."Ind" = c."Ind"

Demo