如何在Oracle SQL中用JOIN替换IN子句?

时间:2018-06-16 08:05:19

标签: sql oracle oracle11g hive hiveql

我正在尝试重写下面的查询,用内连接替换'IN'子句

select * from employee_rec er 
  inner join ed_claim_recd ed on er.ssn=ed.insssn and substr(er.group_rec_key,1,10) = substr(er.group_rec_key,1,10) 
    and ed.claim in (select claimno from cd_claim_recd cd where cd.closedt is not null and cd.closedt != '0000000' and cd.closedt >= '2130101')
    and ed.insssn in (select er1.ssn from employee_rec er1 where er1.status != 'ACTIV' and trim(ER1.CLAIMNO) is null)
    and er.sysind not in ('ABC,'BCD')

以下是我可以提出的结果,但结果与之前的查询不同

select * from employee_rec er
  inner join ed_claim_recd ed on er.ssn = ed.insssn and substr(er.group_rec_key, 1, 10) = substr(er.group_rec_key, 1, 10)
  inner join (select claimno from cd_claim_recd cd where cd.closedt is not null and cd.closedt != '0000000' and cd.closedt >= '2130101') cr on ed.claim = cr.claimno
  inner join (
    select insssn from ed_claim_recd ed2
      inner join (
        select ssn from employee_rec er1
        where
          er1.status != 'ACTIV'
          and trim(ER1.CLAIMNO) is null
      ) er2 on ed2.insssn = er2.ssn
  ) ed3 on ed.insssn = ed3.insssn
  and er.sysind not in ('ABC', 'BCD')

这是重写查询的正确方法还是我太过分了?另外,它是一种有效的方法来重写查询以将“IN”替换为“INNER JOIN”吗?

4 个答案:

答案 0 :(得分:5)

IN子查询和INNER JOIN的工作方式不同。 Join将从一个表中为每个连接键输出具有来自连接表的相同键的所有行。因此,如果连接表中的连接键不唯一,则Join可以复制行。 IN子查询不会重复行。

例如,如果在cr加入子查询

inner join (select claimno from cd_claim_recd cd where cd.closedt is not null and cd.closedt != '0000000' and cd.closedt >= '2130101') cr on ed.claim = cr.claimno

claimno不是唯一的,然后匹配claimno的已加入行将被复制。这是很正常的加入行为。

要避免此类重复,请通过添加DISTINCTrow_number()过滤器,group by等确保加入密钥是唯一的:

inner join (select DISTINCT claimno from cd_claim_recd cd where cd.closedt is not null and cd.closedt != '0000000' and cd.closedt >= '2130101') cr on ed.claim = cr.claimno

其他此类连接也一样。

在这种情况下,INJoin的结果应该相同。

顺便说一句,你不需要所有这些条件:

where cd.closedt is not null and cd.closedt != '0000000' and cd.closedt >= '2130101'

因为' 2130101'大于' 0000000'如果cd.closedt> =' 2130101'它不能为NULL。 cd.closedt >= '2130101'已经足够了。

找到了另一个可能的问题:

and trim(ER1.CLAIMNO) is null

在Hive中(你用@hive标签标记了你的问题)空字符串和null是两个不同的东西。

在Hive中

('' is not NULL) = true

我建议将其替换为and (ER.CLAIMNO is null or trim(ER1.CLAIMNO)='') 空字符串是Hive中的正常值,这就是空字符串参与连接的原因。如果您不需要将它们连接起来,请在加入前转换为NULL或过滤它们。

ed3子查询包含冗余连接,它与原始IN子查询不同。

也许还有其他问题。逐个测试所有连接以找到所有连接

答案 1 :(得分:1)

我这样做(未经测试):

select er.*, ed.*
from   employee_rec er 
       join ed_claim_recd ed
            on  ed.insssn = er.ssn
            and substr(ed.group_rec_key,1,10) /* was er.group_rec_key */ = substr(er.group_rec_key,1,10)
       join cd_claim_recd cd
            on  cd.claimno = ed.claim
       join employee_rec er1
            on  er1.ssn = ed.insssn
where  er.sysind not in ('ABC', 'BCD')
-- and    cd.closedt is not null   -- redundant
-- and    cd.closedt != '0000000'  -- redundant
and    cd.closedt >= '2130101'
and    er1.status != 'ACTIV'
and    trim(er1.claimno) is null

如果cd_claim_recd.claimnoemployee_rec.ssn而非唯一键,那么您可能需要一些重复数据删除逻辑。

答案 2 :(得分:1)

以下内容消除了IN

select er.*, ed.*
  from employee_rec er 
  inner join ed_claim_recd ed
    on er.ssn=ed.insssn and
       substr(ed.group_rec_key,1,10) = substr(er.group_rec_key,1,10)
  INNER JOIN (select claimno
                from cd_claim_recd cd
                where cd.closedt is not null and
                      cd.closedt != '0000000' and
                      cd.closedt >= '2130101') j1
    ON j1.CLAIMNO = ed.claim
  INNER JOIN (select er1.ssn from employee_rec er1
                where er1.status != 'ACTIV' and
                      trim(ER1.CLAIMNO) is null) and
                      er.sysind not in ('ABC,'BCD')) j2
    ON j2.SSN = ed.insssn

正如@GordonLinoff正确地指出的那样,这可能根本不会影响性能。

祝你好运。

答案 3 :(得分:1)

在hive的情况下,我们需要用INNER join替换IN clasue。您可以按如下方式重写上述查询。我认为我们不需要两次加入 employee_rec 表来获得结果。

select er.*,ed.* from employee_rec er
inner join 
ed_claim_recd ed
on er.ssn = ed.insssn
inner join cd_claim_recd cd
on ed.claim = cd.claimno
where cd.closedt is not null 
    and cd.closedt != '0000000' 
    and cd.closedt >= '2130101'
    and er.status != 'ACTIV' and trim(er.CLAIMNO) is null
    and er.sysind not in ('ABC,'BCD')

正如我们消除一个连接条件一样,这是实现结果的紧凑而有效的方法。由于我不知道数据,我没有测试过。如果您有重复的CLAIMS和SSN,那么您需要处理它。正如@leftjoin所提到的,您可以通过消除CLOSEDT上的冗余条件来进一步提高性能