【问题标题】:How to JOIN tables using a range of dates without having procedures如何在没有过程的情况下使用一系列日期连接表
【发布时间】:2013-06-08 04:37:47
【问题描述】:

我有两张桌子,我们称它们为 A 和 B 表:-

表 A 如下所示:

   A.Num1       |   A.Num2      |   A.Date      
   12345        |   38170       |   28/05/2013      
   12345        |   38170       |   29/05/2013      
   12345        |   38170       |   31/05/2013      
   12345        |   38170       |   01/06/2013      
   12345        |   38170       |   03/06/2013      
   12345        |   38170       |   04/06/2013  
   12345        |   38170       |   04/06/2013          
   12345        |   38170       |   07/06/2013  

表 2 (B) 如下所示:

    B.Num1      |   B.Num2      |   B.Status    |   B.Date
    12345       |   38170       |   New         |   28/05/2013  
    12345       |   38170       |   Closed      |   31/05/2013  
    12345       |   38170       |   Reopened    |   04/06/2013

我需要一个这样的表作为输出 - 它基本上应该包含表 A 中的所有行 + 表 B 中的状态

    Num1        |   Num2        |   Status      |   Date
    12345       |   38170       |   New         |   28/05/2013      
    12345       |   38170       |   New         |   29/05/2013      
    12345       |   38170       |   Closed      |   31/05/2013      
    12345       |   38170       |   Closed      |   01/06/2013      
    12345       |   38170       |   Closed      |   03/06/2013      
    12345       |   38170       |   Reopened    |   04/06/2013  
    12345       |   38170       |   Reopened    |   04/06/2013      
    12345       |   38170       |   Reopened    |   07/06/2013      

另外,最好通过简单的SQL语句,而不是程序。

提前致谢。如有任何澄清,请发表评论

因德鲁

更新

当表 B 中的两行状态更改但在同一日期时,我在联接中得到不一致的值。

举个例子:

我有两张表,比如 A 和 B。它们是相当大的表,它们记录了某些信息。表 A 每隔几天扫描一次数据但缺少状态列的文档 如果有更改,则表 B 将获得一个带有状态列的新条目。 我需要匹配这两个并为表 A 中对应于 B 的每个条目派生一个状态。

表 B

Id  | Num1  | Num2  | CreatedOn   | Status
100 | 12345 | 38170 | 28/05/2013  | New
150 | 12345 | 38170 | 28/05/2013  | Closed
200 | 12345 | 38170 | 31/05/2013  | Reopened
250 | 12345 | 38170 | 04/06/2013  | Closed

表 A

Id  | Num1  | Num2  | CreatedOn
55  | 12345 | 38170 | 28/05/2013
99  | 12345 | 38170 | 30/05/2013
145 | 12345 | 38170 | 31/05/2013
192 | 12345 | 38170 | 31/05/2013
223 | 12345 | 38170 | 04/06/2013

所需的输出

Id  | Num1  | Num2  | CreatedOn   | Status
55  | 11552 | 38170 | 28/05/2013  | New
99  | 11552 | 38170 | 30/05/2013  | Closed
145 | 11552 | 38170 | 31/05/2013  | Reopened
192 | 11552 | 38170 | 31/05/2013  | Reopened
223 | 11552 | 38170 | 04/06/2013  | Closed

相反,我得到:(使用 Jeffrey Kamp 的回复)

Id  | Num1  | Num2  | CreatedOn   | Status
55  | 11552 | 38170 | 28/05/2013  | Closed
99  | 11552 | 38170 | 30/05/2013  | Closed
145 | 11552 | 38170 | 31/05/2013  | Reopened
192 | 11552 | 38170 | 31/05/2013  | Reopened
223 | 11552 | 38170 | 04/06/2013  | Closed

还有这个:(使用回复 2 - Mark Ba​​nnister)

Id  | Num1  | Num2  | CreatedOn   | Status
55  | 11552 | 38170 | 28/05/2013  | New
99  | 11552 | 38170 | 30/05/2013  | New
145 | 11552 | 38170 | 31/05/2013  | Reopened
192 | 11552 | 38170 | 31/05/2013  | Reopened
223 | 11552 | 38170 | 04/06/2013  | Closed

很明显,记录 Id-55 可以是新的或关闭的,因为表 B 中有两个条目在同一天 (28/05/13)。但逻辑是它从新 -> 关闭 -> 重新打开 那么有没有办法做到这一点呢?

【问题讨论】:

  • 但是,考虑到您想要的输出,您如何将状态值“新”与日期“29/05/2013”​​进行逻辑关联?
  • 我会考虑不同的设计,因为加入日期范围会导致性能不佳
  • 请不要冒险进入表格的语义。我可以向您保证,这是所需的输入和输出!
  • 同意,但我被卡住了。该系统已经实施。现在更改架构不是一种选择...
  • 是新的、关闭的和重新打开的唯一可能的状态吗?

标签: sql database oracle join


【解决方案1】:

试试:

select a.Num1, a.Num2, a."Date", b.Status
from TableA a
join (select TableB.*, 
             lead("Date",1) over (partition by Num1, Num2 
                                  order by "Date") NextDate
      from TableB) b
  on a.Num1 = b.Num1 and 
     a.Num2 = b.Num2 and 
     a."Date" >= b."Date" and 
     a."Date" < coalesce(b.NextDate,a."Date"+1)

SQLFiddle here.

【讨论】:

  • 根据您的链接,它可以工作。但我不确定这是否是 Oracle SQL 语法。我收到错误“无效的 SQL 语句”
  • 还有为什么日期在引号中?
  • @rtindru:您使用的是哪个版本的 Oracle?如果您查看链接的左上角,您应该会看到它在 Oracle 11g 中运行;它绝对在 Oracle 11 中有效。(我认为它应该在 Oracle 10 以及 Jeffrey 方法适用的任何 Oracle 版本中有效。)如果您可以包含错误消息的完整详细信息,它或许可以确定原因。 “日期”用引号引起来,因为 date 是 Oracle 中的保留字 - 我希望数据库中的实际列名不是保留字。
  • 啊,好吧,列名不是日期。我只是缩短了它。将再次检查并回复您。我认为它是 Oracle 11。
  • +1 这是一个很好的方法,我怀疑性能比我的好。 “b”子查询基本上添加了一个“To”日期,以便每一行代表一个日期范围。如果某些记录失败,您需要隔离它们,并可能为它们运行子查询以查看发生了什么问题。
【解决方案2】:

对于初学者来说有点简单:

SELECT A.Num1
      ,A.Num2
      ,NVL(
         (SELECT DISTINCT
                 FIRST_VALUE(B.Status)
                 OVER (ORDER BY B.Date DESC)
          FROM   B
          WHERE  B.Num1 = A.Num1
          AND    B.Num2 = A.Num2
          AND    B.Date <= A.Date
         ),'New') AS Status
      ,A.Date
FROM A;

可能有更好的方法可能会更快。

【讨论】:

  • 这种方法过于简单,可能只适用于您一次只对一条记录感兴趣的情况——但对于报告,您应该使用 Bannister 的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-24
  • 2020-10-12
相关资源
最近更新 更多