加入默认值但不想要交叉产品

时间:2016-03-30 13:23:10

标签: sql postgresql

我在三个领域加入了两张桌子。我遇到的问题是,如果a.baz不在b.baz中,我需要使用表b中的默认行。问题是有些人会在b中匹配,但也有一个默认值,这会导致我不想要的交叉产品。

select a.foo, a.bar, a.baz, b.fee, b.fie
from a
join b
on a.foo = b.foo
and a.bar = b.bar
and ((a.baz = b.baz) or b.baz = 'DEFAULT')

当前输出:

foo  bar  baz   fee   fie 
bob  doe  NYC   500   200 
bob  doe  DEFUALT 100 100 
john doe  DEFAULT 100 100 
jane doe  NYC   500   500

期望的输出:

foo  bar  baz   fee   fie 
bob  doe  NYC   500   200 
john doe  DEFAULT 100 100 
jane doe  NYC   500   500

示例数据:

a: foo bar baz bob doe NYC john doe NYC jane doe NYC

b: foo bar baz fee fie bob doe NYC 500 200 bob doe DEFAULT 100 100 john doe CHI 300 200 john doe DEFAULT 100 100 jane doe NYC 500 100

3 个答案:

答案 0 :(得分:2)

您必须添加NOT EXISTS,以便在匹配b也存在时排除baz = 'DEFAULT' a.baz = b.baz条记录:

select a.foo, a.bar, a.baz, b.baz, b.fee, b.fie
from a
join b
on a.foo = b.foo and a.bar = b.bar and ((a.baz = b.baz) OR b.baz = 'DEFAULT')
where not exists (select 1
                  from b as b1
                  where a.foo = b1.foo and
                        a.bar = b1.bar and 
                        b.baz = 'DEFAULT' and
                        b1.baz = a.baz)

Demo here

答案 1 :(得分:0)

您的查询非常好,因为它已包含您想要的行。现在,您必须删除不需要的行。您可以通过对匹配进行排名并保持更好的匹配来实现此目的。这可以使用ROW_NUMBER来完成,从而提供更好的记录行号。

select foo, bar, abaz, bbaz, fee, fie
from
(
  select 
    a.foo, a.bar, a.baz as abaz, b.baz as bbaz, b.fee, b.fie,
    row_number() over (partition by a.foo, a.bar 
                       order by case when b.baz = 'DEFAULT' then 2 else 1 end) as rn
  from a
  join b on b.foo = a.foo
         and b.bar = a.bar
         and b.baz in (a.baz, 'DEFAULT')
) ranked
where rn = 1;

答案 2 :(得分:0)

以下是使用外连接解决此问题的另一种简单方法:

select 
  a.foo, 
  a.bar, 
  case when b.baz is null then d.baz else b.baz end as baz,
  case when b.baz is null then d.fee else b.fee end as fee,
  case when b.baz is null then d.fie else b.fie end as fie
from a
left join b    on b.foo = a.foo and b.bar = a.bar and b.baz = a.baz
left join b d  on d.foo = a.foo and d.bar = a.bar and d.baz = 'DEFAULT';

如果费用和fie不可为空,您可以将其简化为:

select 
  a.foo, 
  a.bar, 
  coalesce(b.baz, d.baz) as baz,
  coalesce(b.fee, d.fee) as fee,
  coalesce(b.fie, d.fie) as fie
from a
left join b    on b.foo = a.foo and b.bar = a.bar and b.baz = a.baz
left join b d  on d.foo = a.foo and d.bar = a.bar and d.baz = 'DEFAULT';