【问题标题】:Oracle Join Condition with Top 1Oracle Join 条件与 Top 1
【发布时间】:2016-02-12 19:29:45
【问题描述】:

我试图找到解决方案,包括在我的联接中使用 row_number() 方法,但不知何故无法获得预期的结果。

我现在遇到的问题是基于这样一个事实,即连接表中的一些记录具有多个实例(1 到多个),具有相同的标准,这会导致重复,所以即使我认为我可以在日期上使用 min() 或 max(),我有相同 EffectDT 的情况会出现两次。

除此之外,如果我有没有“活动”记录的实例(XPIRDT IS NOT NULL),我需要提取过期记录,所以我会使用“ OR" 语句,或者在第一次连接没有产生任何记录并且在显示之前有一个案例条件来评估值的情况下简单地执行另一个连接来获取过期的记录?

这是我正在处理的示例数据的一个实例:

因此,上面的数据样本仍然会产生 2 条记录,我可以通过评估到期日期来消除一条,如果所有记录都已过期,则会出现问题,因此不会检索任何内容.

 LEFT OUTER JOIN PARTYXREF
      ON MBR_PERSON.NAMEID = PARTYXREF.NAMEID
     AND PARTYXREF.REFTYPE LIKE 'COMM'
 LEFT JOIN (
                SELECT *
                FROM ADDRDATA TMP
                WHERE TMP.ADDRTYPE = '2'
                AND TMP.EFFECTDT = (SELECT MAX(EFFECTDT) FROM ADDRDATA TMP2 WHERE TMP2.ADDRID = TMP.ADDRID)
             )  MBR_ADDR
       ON PARTYXREF.REFKEY = MBR_ADDR.ADDRID  

我也尝试在我的“JOIN”语句中使用以下内容,但是我无法在我在这里硬编码值的地方使用连接键 (REFKEY),所以这确实有效,但可以'似乎没有包含在 join 语句中。

SELECT ADDR.*
FROM (SELECT tmp.*, row_number() OVER (ORDER BY XPIRDT DESC) AS SEQNUM
        FROM ADDRDATA tmp
        WHERE tmp.ADDRTYPE = '2'
          AND tmp.ADDRID = 10948448
       ) ADDR
WHERE SEQNUM = 1

我在这方面浪费了太多时间,所以我需要其他人的看法! :)

【问题讨论】:

  • 您错误地使用了ROW_NUMBER 函数。您还应该按其他列对数据进行分区(因此ROW_NUMBER 函数可以按您的日期对满足某些条件的行进行排序) - 例如ROW_NUMBER() OVER (PARTITION BY ADDRID, EFFECTDT ORDER BY XPIRD DESC)。最终,类似的DENSE_RANK 可以解决问题。

标签: sql oracle oracle11g greatest-n-per-group


【解决方案1】:

我仍然不清楚在你的例子中你想要哪条记录,所以让我给你一些替代方案。

我从您的问题陈述中收集到,您想要这些可用的最新生效日期。由于涉及关系,您不能使用max()(正如您已经说过的那样),而row_number() 是要走的路:

with cte as (
  select
    addrid, effectdt, xpirdt,
    row_number() over (partition by addrid order by effectdt desc) as rn
  from addrdata
)
select
  addrid, effectdt, xpirdt
from cte
where rn = 1

下一部分是你用空值迷失我的地方......如果你的二级排序是按到期日期,并且你想要一个空值来胜过最新的到期日期,那么你会按到期日期排序并输入@987654324 @:

with cte as (
  select
    addrid, effectdt, xpirdt,
    row_number() over 
        (partition by addrid order by effectdt desc, xpirdt nulls first) as rn
  from addrdata
)
select
  addrid, effectdt, xpirdt
from cte
where rn = 1

这意味着这一行是赢家:

10948448    5/14/2015   <null>

但是,如果您希望仅在没有到期日期时才考虑空值,则可以使用 nulls last(或省略它,因为它是默认值):

with cte as (
  select
    addrid, effectdt, xpirdt,
    row_number() over
        (partition by addrid order by effectdt desc, xpirdt nulls last) as rn
  from addrdata
)
select
  addrid, effectdt, xpirdt
from cte
where rn = 1

意思是这个人得奖了:

10948448    5/14/2015   5/13/2015

由于这使用了row_number(),因此您不会丢失任何行——每行都保证有一个行号。只是如果有真正的联系,那么选择哪一行是一个折腾。但是,您的无效到期日期问题不应导致此方法出现任何问题。

-- 2/13/16 编辑--

我想我开始了解您的问题,但我不是 100% 确定。我已将您的代码的 sn-ps 与我的建议与左连接合并,并且需要首先设置空过期日期,这是我的下一个破解:

with cte as (
  select
    addrid, effectdt, xpirdt,
    row_number() over 
        (partition by addrid order by effectdt desc, xpirdt nulls first) as rn
  from addrdata
)
select
  cte.addrid, effectdt, xpirdt
from
  mbr_person mb
  left join partyxref px on
    mb.nameid = px.nameid and
    px.reftype = 'COMM'
  left join cte on
    px.refkey = cte.addrid and
    cte.rn = 1

假设不这样做:

  1. 当您说空 XPIRDT 具有优先权时,您的意思是甚至在最近的生效日期,还是只是最近的 EFFECTDT 的决胜局?如果是后者,那么我所拥有的应该可以工作。如果是前者,那么我们需要在解析函数中切换排序方式
  2. 我完全猜到partyxrefmbr_person 表。如果这不能解决问题,也许发布一些示例数据和所需的输出以包含这两个表,或者摆弄它?

【讨论】:

  • 非常感谢,是的,NULL XPIRDT 将优先于任何其他记录,因为它被认为是“活动”记录,但是如果我没有任何活动记录,我' d 通过 EFFECTDT 拉下一个过期的。就您的建议而言,您能否提供在 ADDRID 上使用 LEFT JOIN 的语句?我尝试了一些变体,试图在产生记录的子选择中考虑它,但不知何故,我一直没有得到任何行,因为我假设行号优先于我在建立之前尝试检索的记录集行序列。
  • 我的第二次尝试已发布.​​..并且我有一些潜在的后续问题可能无法 100% 解决您的问题
  • 再次感谢!!考虑到所涉及的记录数量,我实际上使用您建议的方法用不同的地址填充了一个全局临时表,然后我将临时表加入到我的主查询中以获取地址数据。今天早上根据给定的场景进行了几次测试,一切似乎都很完美!谢谢!!
猜你喜欢
  • 2015-04-02
  • 2021-09-20
  • 2015-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
  • 2016-01-02
相关资源
最近更新 更多