使用外连接时where子句中的'OR'的SQL替代方法

use*_*914 8 sql oracle select join

我有以下查询:

select * from
from assignments dah, employees emp 
where 
    dah.person_id=emp.person_id(+)
and 
(dah.effective_end_date between emp.date_from(+) and emp.date_to(+)
and dah.effective_end_date between emp.valid_from(+) and emp.valid_to(+))
or   
(dah.effective_start_date between emp.date_from(+) and emp.date_to(+)
and dah.effective_start_date between emp.valid_from(+) and emp.valid_to(+))
Run Code Online (Sandbox Code Playgroud)

我得到以下消息:'外部连接运算符(+)不允许在OR或IN'的操作数中.我知道使用2个带有内连接的联合是一个解决方案,但我不能使用它,因为我实际上有很多代码(我提供的代码只是一个例子).

编辑:我需要通过oracle语法完成此操作,因为我使用数据仓库并且我们的ETL不完全支持显式语法.也许有一些我没有看到的东西,这可以用不同的方式写出来?

编辑nr.2:可能以某种方式实现日期重叠逻辑而不使用OR和oracle语法?

Gor*_*off 5

使用显式left join语法:

select *
from employees emp left join
     assignments dah 
     on dah.person_id = emp.person_id and
        ((dah.effective_end_date between emp.date_from and emp.date_to and
          dah.effective_end_date between emp.valid_from and emp.valid_to
         ) or
         (dah.effective_start_date between emp.date_from and emp.date_to and
          dah.effective_start_date between emp.valid_from and emp.valid_to
         )
        );
Run Code Online (Sandbox Code Playgroud)

一个简单的规则是永远不要在from子句中使用逗号.始终使用显式join语法.

注意:从技术上讲,您的外连接语法将具有其他顺序的表:

from assignments dah left join
     employees emp 
     on . . .
Run Code Online (Sandbox Code Playgroud)

我故意交换它们.在left join第一个表保留所有行,甚至包括那些没有匹配.该+语法是难以效仿.在+上,将得到的侧逐渐变NULL值.但是,对我来说,不匹配的行似乎不太可能在赋值表中.

如果您有适当的外键关系,那么所有作业都应该有一个正确的人.但是,我可能不了解您的数据,您可能想要反转表格以了解您真正想要做的事情.

编辑:

至于重叠,我倾向于使用更简单的:

     on dah.person_id = emp.person_id and
        (dah.effective_end_date >= emp.date_from and
         dah.effective_start_date <= emp.date_to 
        )
Run Code Online (Sandbox Code Playgroud)

+如果您愿意,甚至可以使用古老的符号来写这个.另请注意:这些并不完全相同.这将检测一个周期完全嵌入另一个周期的重叠.


Bon*_*ist 2

由于您必须使用旧式外连接语法,因此这是一种方法(经过简化,因为您没有向我们提供示例数据和/或表创建脚本):

with assignments as (select 1 assignment_id, 1 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 2 assignment_id, 1 person_id, to_date('02/08/2015', 'dd/mm/yyyy') start_date, to_date('04/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 3 assignment_id, 1 person_id, to_date('06/08/2015', 'dd/mm/yyyy') start_date, to_date('10/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 4 assignment_id, 2 person_id, to_date('02/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual),
       employees as (select 1 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 3 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual)
select *
from   assignments dah,
       employees emp
where  dah.person_id = emp.person_id (+)
and    dah.start_date <= emp.end_date (+)
and    dah.end_date >= emp.start_date (+);

ASSIGNMENT_ID  PERSON_ID START_DATE END_DATE   PERSON_ID_1 START_DATE_1 END_DATE_1
------------- ---------- ---------- ---------- ----------- ------------ ----------
            2          1 02/08/2015 04/08/2015           1 01/08/2015   03/08/2015
            1          1 01/08/2015 03/08/2015           1 01/08/2015   03/08/2015
            3          1 06/08/2015 10/08/2015                                    
            4          2 02/08/2015 03/08/2015          
Run Code Online (Sandbox Code Playgroud)

您确定您的外连接正确吗?您确定您实际上不是在追求以下内容吗?:

with assignments as (select 1 assignment_id, 1 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 2 assignment_id, 1 person_id, to_date('02/08/2015', 'dd/mm/yyyy') start_date, to_date('04/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 3 assignment_id, 1 person_id, to_date('06/08/2015', 'dd/mm/yyyy') start_date, to_date('10/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 4 assignment_id, 2 person_id, to_date('02/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual),
       employees as (select 1 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual union all
                     select 3 person_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('03/08/2015', 'dd/mm/yyyy') end_date from dual)
select *
from   assignments dah,
       employees emp
where  dah.person_id (+) = emp.person_id
and    dah.start_date (+) <= emp.end_date
and    dah.end_date (+) >= emp.start_date;

ASSIGNMENT_ID  PERSON_ID START_DATE END_DATE   PERSON_ID_1 START_DATE_1 END_DATE_1
------------- ---------- ---------- ---------- ----------- ------------ ----------
            1          1 01/08/2015 03/08/2015           1 01/08/2015   03/08/2015
            2          1 02/08/2015 04/08/2015           1 01/08/2015   03/08/2015
                                                         3 01/08/2015   03/08/2015
Run Code Online (Sandbox Code Playgroud)