在oracle sql中加入2个表

时间:2016-08-19 17:24:31

标签: sql oracle join

这是我开始的配置:

DROP TABLE  ruleset1;
CREATE TABLE  ruleset1  (id int not null unique,score_rule1 float default 0.0,score_rule2 float default 0.0,score_rule3 float default 0.0);
DROP TABLE  ruleset2;
CREATE TABLE  ruleset2  (id int not null unique,score_rule1 float default 0.0,score_rule2 float default 0.0,score_rule3 float default 0.0);

insert into ruleset1 (id, score_rule1, score_rule2, score_rule3) values (0,0.8,0,0);
insert into ruleset1 (id, score_rule1, score_rule2, score_rule3) values (1,0,0.1,0);
insert into ruleset2 (id, score_rule1, score_rule2, score_rule3) values (0,0,0,0.3);
insert into ruleset2 (id, score_rule1, score_rule2, score_rule3) values (2,0,0.2,0);

我现在拥有的是2张桌子 ruleset1:

| ID | SCORE_RULE1 | SCORE_RULE2 | SCORE_RULE3 
================================================
| 0  |    0.8      |     0       |     0     
| 1  |    0        |     0.1     |     0

和ruleset2:

| ID | SCORE_RULE1 | SCORE_RULE2 | SCORE_RULE3 
================================================
| 0  |    0        |     0       |     0.3     
| 2  |    0        |     0.2     |     0

我希望外连接它们并计算非零列的平均值,如下所示:

| ID | Average  
================
| 0  |    0.55 
| 1  |    0.1 
| 2  |    0.2 

我目前的查询是:

select * from ruleset1 full outer join ruleset2 on ruleset1.id = ruleset2.id;

给出了一个丑陋的结果:

| ID | SCORE_RULE1 | SCORE_RULE2 | SCORE_RULE3 | ID | SCORE_RULE1 | SCORE_RULE2 | SCORE_RULE3
============================================================================================
| 0  |     .8      |       0     |      0      | 0  |      0      |      0      |   .3
| -  |     -       |       -     |      -      | 2  |      0      |      .2     |   0
| 1  |     0       |       .1    |      0      | -  |      -      |      -      |   -

有人可以提供更好的查询吗? 非常感谢你!

5 个答案:

答案 0 :(得分:3)

当然avg不会忽略零,只会忽略NULL,因此可以使用NULLIF(column, 0)

但是,当你得到非规范化数据时,你可以简单地将其正常化:

select id, avg(score)
from
 (
    select id, score_rule1   score 
        from ruleset1 where score_rule1 <> 0
    union all 
    select id, score_rule2 from ruleset1 where score_rule2 <> 0
    union all
    select id, score_rule3 from ruleset1 where score_rule3 <> 0
    union all 
    select id, score_rule1 from ruleset2 where score_rule1 <> 0
    union all 
    select id, score_rule2 from ruleset2 where score_rule2 <> 0
    union all 
    select id, score_rule3 from ruleset2 where score_rule3 <> 0
 ) dt
group by id;

为了避免五个联盟,你只能使用一个并做一些额外的逻辑:

select id, sum(score) / sum(score_count)
from
 (
    select id, score_rule1 + score_rule2 + score_rule3   score,
       case when score_rule1 = 0 then 0 else 1 end +
       case when score_rule2 = 0 then 0 else 1 end +
       case when score_rule3 = 0 then 0 else 1 end   score_count
    from ruleset1 

    union all 

    select id, score_rule1 + score_rule2 + score_rule3 score,
       case when score_rule1 = 0 then 0 else 1 end +
       case when score_rule2 = 0 then 0 else 1 end +
       case when score_rule3 = 0 then 0 else 1 end   score_count
    from ruleset2
 ) dt
group by id;

这假设core_rule列中没有NULL。

答案 1 :(得分:0)

这是PostgreSQL的一个例子,你可以适应Oracle(抱歉,SQLFiddle的甲骨文没有合作)。感谢Juan Carlos Oropeza的建议,下面的代码很好地运行在Oracle上:http://rextester.com/DVP59353

select
  r.id,
  sum(coalesce(r1.score_rule1,0) + 
      coalesce(r1.score_rule2,0) + 
      coalesce(r1.score_rule3,0) +
      coalesce(r2.score_rule1,0) + 
      coalesce(r2.score_rule2,0) + 
      coalesce(r2.score_rule3,0)
      )
      /
  sum(case when coalesce(r1.score_rule1,0) <> 0 then 1 else 0 end +
      case when coalesce(r1.score_rule2,0) <> 0 then 1 else 0 end +
      case when coalesce(r1.score_rule3,0) <> 0 then 1 else 0 end +
      case when coalesce(r2.score_rule1,0) <> 0 then 1 else 0 end +
      case when coalesce(r2.score_rule2,0) <> 0 then 1 else 0 end +
      case when coalesce(r2.score_rule3,0) <> 0 then 1 else 0 end) as Average

from

  (select id from ruleset1
  union
  select id from ruleset2) r

left join ruleset1 r1 on r.id = r1.id
left join ruleset2 r2 on r.id = r2.id

group by r.id

PostFSQL版本的SQLFiddle在这里:http://sqlfiddle.com/#!15/24e3f/1

此示例使用union组合来自两个表的id。这样做允许ruleset1和ruleset2中的相同ID在结果中只出现一次。 r是为此生成的表提供的别名。

然后将所有id与两个表连接起来。在求和过程中,左连接产生的NULL值可能会影响结果。因此,NULL在数学中合并为零。

答案 2 :(得分:0)

dnoeth是一个简单而干净的答案。

这里我只是在玩COALESCENVL2

select COALESCE(r.ID, s.ID),
  COALESCE(r.score_rule1, 0) + 
  COALESCE(r.score_rule2, 0) + 
  COALESCE(r.score_rule3, 0) +
  COALESCE(s.score_rule1, 0) + 
  COALESCE(s.score_rule2, 0) + 
  COALESCE(s.score_rule3, 0) as sum,

  NVL2(r.score_rule1, 0, 1) + 
  NVL2(r.score_rule2, 0, 1) + 
  NVL2(r.score_rule3, 0, 1) + 
  NVL2(s.score_rule1, 0, 1) + 
  NVL2(s.score_rule2, 0, 1) + 
  NVL2(s.score_rule3, 0, 1) as tot

from ruleset1 r
full outer join ruleset2 s
  on ruleset1.id = ruleset2.id;

然后您的平均值为sum/tot

答案 3 :(得分:0)

union all您的两个表,取消忽略,使用nullif将零更改为null,并使用标准avg()聚合函数:

select id, avg(nullif(value, 0)) as avg_value from (
  select * from ruleset1
  union all
  select * from ruleset2
)
unpivot ( value for column_name in (score_rule1, score_rule2, score_rule3))
group by id
order by id
;

        ID  AVG_VALUE
---------- ----------
         0        .55
         1         .1
         2         .2

答案 4 :(得分:-1)

SELECT s.id, AVG(s.score)
FROM(
SELECT id,score_rule1+score_rule2+score_rule3 as score
FROM ruleset2
UNION ALL
SELECT id,(score_rule1+score_rule2+score_rule3) as score
FROM ruleset1) s

group by s.id