【问题标题】:Equivalent date function in oracle for pd.merge_asoforacle 中 pd.merge_asof 的等效日期函数
【发布时间】:2022-01-06 11:35:33
【问题描述】:

oracle 中是否有任何等效的日期函数用于 python pd.merge_asof?请看下面的例子:

表 A1

ID      Date
1       12/02/2020
2       11/23/2019
3       09/09/2021
3       10/12/2021

表 A2

ID      Date
3       09/12/2021

对于 ID = 3,A2 表中的日期为 09/12/2021。当我尝试将此 ID 和日期与 A1 匹配时,只有 ID 匹配。所以我尝试添加一个逻辑来获得以下输出(因为它是最接近的日期)。

输出

ID       Date        ID2      Date2
3     09/09/2021      3     09/12/2021

【问题讨论】:

  • 不确定我是否理解这个问题。如果您不参考其他语言(如 python),您将获得更多帮助,而是简单地用简单的英语陈述问题。鉴于表 A1 和 A2 中的日期,您是否正在寻找 - 对于 A2 中的每个日期 - 以在表 A1 中找到最接近的日期,即使没有完美匹配?如果是这样,有几个问题:(1)您是在看“之前”和“之后”,还是只看一个方向(例如最近的日期,但只在表 A2 的日期之前)? (2) 是否需要设置一个限制,比如“最接近,但不超过 30 天”?
  • (3) 如果“最近”(两个、三个或更多 ID 的日期都与目标日期与 A2 的距离相等,都等于最小距离)有联系怎么办?归还所有这些?只返回其中一个,如果是,是哪一个? (4) 只是为了确认 - 除了通过 ID 进行匹配之外,您还要这样做 - 对吧?
  • 是的,我正在寻找 A1 中没有匹配项的最接近的日期。最近的日期我在之前或之后,没有方向。不,没有限制。最接近的日期可能是不同的年份。
  • 如果两个最接近的日期之间存在平局,那么它只为该特定 ID 选择一个日期。

标签: sql oracle plsql


【解决方案1】:

你可以做这样的事情(加入 + 聚合):

with
  t1 (id, datum) as (
    select 1, date '2020-12-02' from dual union all
    select 2, date '2019-11-23' from dual union all
    select 3, date '2021-09-09' from dual union all
    select 3, date '2021-10-12' from dual
  )
, t2 (id, datum) as (
    select 3, date '2021-09-12' from dual
  )
select t2.id, 
       min(t1.datum) keep (dense_rank first
                 order by abs(t1.datum - t2.datum)) as date_1,
       t2.datum as date_2
from   t2 left outer join t1 on t1.id = t2.id
group  by t2.id, t2.datum
;

        ID DATE_1     DATE_2    
---------- ---------- ----------
         3 09-09-2021 12-09-2021

我只在输出中包含了一次id - 因为您是通过id 加入的,所以显示两次是没有意义的。

【讨论】:

  • 谢谢@mathguy。它正在工作 :) 非常感谢您的帮助!
【解决方案2】:

没有这样的功能(至少,据我所知),因此一种选择是对日期之间的差异进行排名并获取排名最高的行。像这样的:

SQL> with
  2  -- sample data
  3  t1 (id, datum) as
  4    (select 1, date '2020-12-02' from dual union all
  5     select 2, date '2019-11-23' from dual union all
  6     select 3, date '2021-09-09' from dual union all
  7     select 3, date '2021-10-12' from dual
  8    ),
  9  t2 (id, datum) as
 10    (select 3, date '2021-09-12' from dual),
 11  --
 12  temp as
 13    -- rank difference of dates in ascending order
 14    (select b.id b_id, b.datum b_datum, a.id a_id, a.datum a_datum,
 15       rank() over (partition by a.id order by abs(b.datum - a.datum) asc) rnk
 16     from t1 a join t2 b on a.id = b.id
 17    )
 18  -- value you want is ranked the "highest"
 19  select a_id, a_datum,
 20         b_id, b_datum
 21  from temp
 22  where rnk = 1;

      A_ID A_DATUM          B_ID B_DATUM
---------- ---------- ---------- ----------
         3 09/09/2021          3 09/12/2021

SQL>

【讨论】:

  • OP 澄清说:在平局的情况下,他只需要选择一个“最佳”行,而不是全部。易于修改:使用row_number() 而不是rank()
【解决方案3】:

从 Oracle 12 开始,您可以使用 LATERAL JOINFETCH FIRST ROW ONLY

SELECT a1.*,
       a2."DATE" AS date2
FROM   a2
       CROSS JOIN LATERAL (
         SELECT a1.*
         FROM   a1
         WHERE  a1.id = a2.id
         ORDER BY ABS(a1."DATE" - a2."DATE")
         FETCH FIRST ROW ONLY
       ) a1

其中,对于样本数据:

CREATE TABLE A1 (ID, "DATE") AS
SELECT 1, DATE '2020-12-02' FROM DUAL UNION ALL
SELECT 2, DATE '2019-11-23' FROM DUAL UNION ALL
SELECT 3, DATE '2021-09-09' FROM DUAL UNION ALL
SELECT 3, DATE '2021-10-12' FROM DUAL;

CREATE TABLE A2 (ID, "DATE") AS
SELECT 3, DATE '2021-09-12' FROM DUAL;

输出:

ID DATE DATE2
3 2021-09-09 00:00:00 2021-09-12 00:00:00

如果您在A2 中有多个相同的ID 行,那么它们将分别与最近的行匹配:

例如,之后:

INSERT INTO a2 (id, "DATE") VALUES (3, DATE '2025-01-01');

那么上面的查询会输出:

ID DATE DATE2
3 2021-09-09 00:00:00 2021-09-12 00:00:00
3 2021-10-12 00:00:00 2025-01-01 00:00:00

db小提琴here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-05
    • 2015-05-19
    • 1970-01-01
    • 2021-01-09
    • 2023-04-03
    • 2012-12-27
    • 1970-01-01
    相关资源
    最近更新 更多