使用日期解决交叉连接

时间:2018-01-10 05:46:40

标签: sql oracle12c

我有3张桌子:

Client (cus_id, First_Name)
sms_Log (sms_log_id, cus_id, sms_time)
Mail_Log (mail_Log_id, cus_id, mailed_time)  

Cus_id是三个表之间的链接,但是,sms_log和Mail_Log之间没有链接导致交叉连接。我需要将邮件发送后收到的所有短信分组。

E.g。数据

Table: Client
cus_id  First_Name
4       'Doe'

Table: sms_log
sms_log_id  cus_id, sms_time
1           4       08/02/2012
2           4       08/03/2012
3           4       08/06/2012

Table: Mail_Log
mail_Log_id cus_id  mailed_time
1           4       08/01/2012
2           4       08/05/2012

加入这些将创建一个包含6条记录的交叉连接。

有没有办法可以写一个查询,可以告诉我前面的两条短信(日期2和3)是在01号收到的邮件中收到的,而第06天的短信是为了回复05日发来的邮件?

预期输出(每封邮件收到的短信数)

Table: output_record
cus_id  mail_Log_id, sms_received
4       1            2
4       2            1

1 个答案:

答案 0 :(得分:0)

在Oracle 12.1及更高版本中,您可以使用match_recognize子句解决此问题。

在您描述的三个表中,您不需要第一个用于"手头的问题"。如果您需要检索客户名称和其他详细信息,可以将下面的查询结果加入customer表;我会假设你能够自己做到这一点。

然后,要从其他两个表中的输入中获取所需的摘要,您根本不需要连接。您需要一个UNION ALL(仔细完成),然后您可以使用嵌套分析函数(在早期版本的Oracle中)或使用match_recognize一次完成所有工作,如下所示。

WITH子句中,我创建了测试数据,因为您没有提供它。请注意,WITH子句不是解决方案的一部分 - 您应该将其删除,并在查询的其余部分确保表名和列名与实际输入匹配。

中间部分是两个日志表的union all(相关列)。然后match_recognize就在最后。它为找到的每个完整匹配产生一行 - 其中a"匹配"由一个邮件事件和随后的所有SMS事件组成,直到下一个邮件事件。所有内容首先由cus_id分区(根据需要),每个此类分区按cus_id按事件时间(邮寄时间或短信时间)排序。我也允许在完全相同的时间记录邮件和短信(非常罕见,但并非不可能)。在这种情况下,我会假设邮件是先发送的,所以短信属于那封邮件(而不是前一封邮件)。如果需要,这可以很容易地改变。

with
  sms_log ( sms_log_id, cus_id, sms_time ) as (
    select 1, 4, to_date('08/02/2012', 'mm/dd/yyyy') from dual union all
    select 2, 4, to_date('08/03/2012', 'mm/dd/yyyy') from dual union all
    select 3, 4, to_date('08/06/2012', 'mm/dd/yyyy') from dual
  ),
  mail_log ( mail_Log_id, cus_id, mailed_time ) as (
    select 1, 4, to_date('08/01/2012', 'mm/dd/yyyy') from dual union all
    select 2, 4, to_date('08/05/2012', 'mm/dd/yyyy') from dual
  )
select cus_id, mail_log_id, sms_received
from (
       select cus_id, mail_log_id, mailed_time as tm
         from mail_log
       union all
       select cus_id, null, sms_time
         from sms_log
     )
match_recognize(
  partition by cus_id
  order by     tm, mail_log_id
  measures     m.mail_log_id as mail_log_id,
               m.tm          as mailed_time,
               count(*) - 1  as sms_received
  pattern      ( m s* )
  define       m as mail_log_id is not null,
               s as mail_log_id is null
)
order by cus_id, mailed_time
;

CUS_ID MAIL_LOG_ID SMS_RECEIVED
------ ----------- ------------
     4           1            2
     4           2            1