【问题标题】:SQL how to join only first matching rowSQL如何只加入第一个匹配的行
【发布时间】:2015-08-31 17:41:04
【问题描述】:

我不明白如何只加入第一个匹配行。如果我通过 person_id 和日期的组合加入,查询将返回多个值。在这种情况下,如果连接返回多个值,我希望只显示此连接中的一行。预期结果 - assignment_id、effective_start_date、effective_end_date 的组合是唯一的。

最困扰我的是我没有唯一标识符,只有日期。如果外籍人士表中的两个日期范围适合分配有效开始日期,有效结束日期范围,我只需要显示一行。

请向我提供 Oracle 语法(如我的代码示例)。

with assignments (assignment_id, person_id, effective_start_date, effective_end_date) as (
   select 456, 123, date '2015-01-01', date '2015-03-15' from dual union all  
   select 456, 123, date '2015-03-16', date '4712-12-31' from dual union all   
   select  975, 123, date '2015-03-16', date '4712-12-31' from dual            
 ),
expatriates (person_id, home_country, host_country, date_from, date_to, valid_from, valid_to)
as
(
   select 123, 'TEST2', 'TEST2',    date '2015-01-01', date '2015-03-15', date '2015-01-01', date   '2015-03-15' from dual union all
   select 123, 'TEST1', 'TEST1',    date '2015-04-16', date '2016-06-15', date '2015-04-16', date   '2016-06-15' from dual union all
   select 123, 'TEST',  'TEST',   date '2015-03-16', date '2016-04-15', date '2015-03-16', date '2015-04-15' from dual
)

select 
    a.assignment_id,
    a.person_id, 
    a.effective_start_date, 
    a.effective_end_date,
    subq.home_country, 
    subq.host_country, 
    subq.date_from, 
    subq.date_to
from assignments a
     , expatriates subq 
where 
    a.person_id=subq.person_id
    and subq.valid_from <= a.effective_end_date
   and subq.valid_to >= a.effective_start_date

【问题讨论】:

  • 读到“我只需要显示一行”就奇怪了,没有进一步的解释。您不想告诉我们在这种情况下选择哪一行吗?顺便说一句:您使用的连接语法已经过时了二十多年。您应该切换到显式连接(例如from assignments a inner join expatriates subq on ...)。

标签: sql oracle join top-n


【解决方案1】:

这是子查询中分析排名函数的经典用例,可以对其进行过滤以返回所需的子集。在这种情况下,我使用了 ROW_NUMBER(),因为您没有提供任何丢弃行的标准,所以大概哪个实际行是“第一”行很重要。

select assignment_id,
        person_id, 
        effective_start_date, 
        effective_end_date,
        date_from, 
        date_to,
        home_country, 
        host_country
from (
    select 
        a.assignment_id,
        a.person_id, 
        a.effective_start_date, 
        a.effective_end_date,
        e.date_from, 
        e.date_to,
        e.home_country, 
        e.host_country,
        row_number() over (partition by a.assignment_id, a.effective_start_date 
                            order by e.date_from) rn
    from assignments a
         join expatriates e
    on  (a.person_id=e.person_id )
    where e.valid_from <= a.effective_end_date
        and e.valid_to >= a.effective_start_date
)
where rn = 1
order by 1, 3, 2
/

这会从您的示例数据中返回以下行:

 28* order by 1, 3, 2

ASSIGNMENT_ID  PERSON_ID EFFECTIVE EFFECTIVE DATE_FROM DATE_TO   HOME_ HOST_
------------- ---------- --------- --------- --------- --------- ----- -----
          456        123 01-JAN-15 15-MAR-15 01-JAN-15 15-MAR-15 TEST2 TEST2
          456        123 16-MAR-15 31-DEC-12 16-MAR-15 15-APR-16 TEST  TEST
          975        123 16-MAR-15 31-DEC-12 16-MAR-15 15-APR-16 TEST  TEST

3 rows selected.

SQL> 

Oracle 拥有大量简洁的分析功能。 Find out more.

【讨论】:

    【解决方案2】:

    我不确定您需要从 expatriates 表中过滤记录的条件,但作为示例,我们可以按 date_from 字段过滤:

    with assignments (assignment_id, person_id, effective_start_date, effective_end_date) as (
       select 456, 123, date '2015-01-01', date '2015-03-15' from dual union all  
       select 456, 123, date '2015-03-16', date '4712-12-31' from dual union all   
       select  975, 123, date '2015-03-16', date '4712-12-31' from dual            
     ),
    
    
    expatriates (person_id, home_country, host_country, date_from, date_to, valid_from, valid_to)
    as
    (
       select 123, 'TEST2', 'TEST2',    date '2015-01-01', date '2015-03-15', date '2015-01-01', date   '2015-03-15' from dual union all
       select 123, 'TEST1', 'TEST1',    date '2015-04-16', date '2016-06-15', date '2015-04-16', date   '2016-06-15' from dual union all
       select 123, 'TEST',  'TEST',   date '2015-03-16', date '2016-04-15', date '2015-03-16', date '2015-04-15' from dual
    
    )
    
    select 
    a.assignment_id,
    a.person_id, 
    a.effective_start_date, 
    a.effective_end_date,
    subq.home_country, 
    subq.host_country, 
    subq.date_from, 
    subq.date_to
    from assignments a, expatriates subq 
    where 
    a.person_id=subq.person_id
    and subq.valid_from <= a.effective_end_date
    and subq.valid_to >= a.effective_start_date
    and subq.date_from = 
    (
      select 
        max(date_from) 
      from expatriates sq2 
        where 
          sq2.person_id = a.person_id and 
          sq2.valid_from <= a.effective_end_date and 
          sq2.valid_to >= a.effective_start_date 
    )
    

    SqlFiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 2017-07-16
      • 1970-01-01
      • 2022-11-23
      相关资源
      最近更新 更多