0

下面的 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

4 回答 4

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;
于 2013-06-01T04:22:33.307 回答
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

于 2013-06-01T04:35:14.887 回答
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

于 2013-06-01T05:00:27.573 回答
0

我认为我没有完全理解您正在努力实现的目标,但如果我错了,请纠正我。

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

根据您想要实现的结果的描述,您需要的是一个外连接。哪一个完全左/右/完全外连接取决于您要实现的目标(看起来您需要左外连接或完全外连接)。我不太确定您的目标是否准确,因为您在这个具体示例中解释了您的数据应该如何看起来不像一般情况。

所以请查看不同连接类型的描述并选择sql 连接类型

还有一个重要说明:文本类型可能是列表中我认为是主键的最后一种类型。

于 2013-06-01T04:23:10.657 回答