限制很多 - SQL连接中的许多结果

时间:2013-06-01 03:23:43

标签: sql

下面的SQL包含一些DDL和一个简单的查询。

我得到的结果是

a1|b1|c1  
a1|b2|c3  
a3|b3|c2  
a3|b3|c3  
a3|b3|c4  
a3|b3|c5  
a3|b5|c6  
a3|b5|c7

我想要的结果是

a1   |b1   |c1  
a1   |b2   |c3  
a3   |b3   |c2   
null |null |c4  
null |null |c5  
a3   |b5   |c6  
null |null |c7

我尝试使用MAX,MIN,rownums等等。我没办法。我只包括我开始使用的基本查询而不是我尝试的所有选项,因为它们根本不起作用。任何帮助表示赞赏!


BEGIN TRANSACTION;

drop table if exists table_A;
drop table if exists table_B;
drop table if exists table_C;

/* Create a table called NAMES */
CREATE TABLE table_A(a_Id text PRIMARY KEY, val_a text);
CREATE TABLE table_B(a_Id text, b_Id text, val_b text);
CREATE TABLE table_C(b_Id text, c_Id text, val_c text);

/* Create few records in this table */
INSERT INTO table_A VALUES('a1','va1');
INSERT INTO table_A VALUES('a2','va2');
INSERT INTO table_A VALUES('a3','va3');

INSERT INTO table_B VALUES('a1', 'b1','vb1');
INSERT INTO table_B VALUES('a1', 'b2','vb2');
INSERT INTO table_B VALUES('a3', 'b3','vb31');
INSERT INTO table_B VALUES('a2', 'b4','vb4');
INSERT INTO table_B VALUES('a3', 'b5','vb31');

INSERT INTO table_C VALUES('b1', 'c1','vc1');
INSERT INTO table_C VALUES('b3', 'c2','vc2');
INSERT INTO table_C VALUES('b3', 'c3','vc3');
INSERT INTO table_C VALUES('b2', 'c3','vc3');
INSERT INTO table_C VALUES('b3', 'c4','vc2');
INSERT INTO table_C VALUES('b3', 'c5','vc3');
INSERT INTO table_C VALUES('b5', 'c6','vc3');
INSERT INTO table_C VALUES('b5', 'c7','vc3');

COMMIT;

select
a.a_Id, b.b_Id, c.c_Id
from
table_A as a
join
table_B as b
on a.a_Id = b.a_Id
join
table_C as c
on b.b_Id = c.b_Id;

4 个答案:

答案 0 :(得分:1)

我首先建议你在演示逻辑中处理这个听起来更好。但是,可以单独使用SQL来完成。

您可以利用Oracle的LAG()函数和CASE来检查上一行是否具有相同的a和b id值。

以下是使用公用表表达式的示例:

with cte as (
  select 
    a.a_Id, b.b_Id, c.c_Id, 
    lag (a.a_Id,1) over (order by a.a_Id, b.b_Id) prev_a_Id,
    lag (b.b_Id,1) over (order by a.a_Id, b.b_Id) prev_b_Id
  from table_A a
    join table_B b
      on a.a_Id = b.a_Id
    join table_C c
      on b.b_Id = c.b_Id
  order by
    a.a_id, b.b_id
  )
select 
  case 
      when prev_a_Id is null or
            prev_a_Id <> a_Id or
            prev_b_Id <> b_Id
      then a_id 
    end new_a_Id,
  case 
      when prev_a_Id is null or
            prev_a_Id = a_Id or
            prev_b_Id = b_Id
      then b_id 
    end new_b_Id, c_Id
from cte;

答案 1 :(得分:1)

这样的事情应该有用(我已经在PostgreSql上测试了它,也应该在Oracle上工作)

SELECT 
  case when row_number = 1 then a_id end as a_id,
  case when row_number = 1 then b_id end as b_id,
  c_id
FROM (
  SELECT 
    a.a_Id, 
    b.b_Id, 
    c.c_Id, 
    row_number() OVER (partition by a.a_id, b.b_id order by c.c_id) as row_number, --for a_id, b_id
    row_number() OVER (partition by c.c_id order by c.c_id) as row_number2         --to avoid c_id duplicates
  FROM
    table_A  a
    join
    table_B  b on a.a_Id = b.a_Id
    join table_C  c on b.b_Id = c.b_Id
  )  innerquery
WHERE 
  row_number2 = 1 --this is to avoid c_id duplicates

SQLFIDDLE

答案 2 :(得分:1)

select  t1.a_id, t1.b_id, table_c.c_id
from table_c 
left join 
(
  select a_Id, b_Id, c_Id
  from 
  (
    select a.a_Id as a_id, b.b_Id as b_id, c.c_Id as c_id,
           ROW_NUMBER() OVER (PARTITION BY  a.a_ID, b.b_id ORDER BY C_ID) as aNum
    from table_A as a
    join table_B as b on a.a_Id = b.a_Id
    join table_C as c on b.b_Id = c.b_Id
  ) t2
  where aNum = 1
) t1 on  table_c.c_id = t1.c_id 
order by table_c.c_id

小提琴:

http://sqlfiddle.com/#!3/6049b/1

答案 3 :(得分:0)

我不认为我完全明白你想要实现,但如果我错了,请纠正我。

首先,您使用内部联接来连接表(至少如果您使用的是sql server,但在oracle中应该是相同的)。这意味着,例如,只有在第二个表中有对应行的情况下才会从第一个表中获取行,而如果现在第二个表中对应行的第一个表中的行,则第二个表中的行永远不会出现在结果中。

根据您想要实现的结果描述,您需要的是外部联接。哪一个左/右/全外连接取决于您尝试实现的(看起来您需要左外连接或全外连接)。我不太确定你的目标是什么,因为你解释了这个具体例子中的数据应该看起来不是一般情况。

因此,请查看不同联接类型的说明,然后选择sql join types

还有一个输入注释:文本类型可能是我认为是主键的列表中的最后一种类型。