Oracle SQL Developer订阅服务器-创建交叉表

时间:2018-07-06 11:14:19

标签: sql oracle

我有一个表UPCALL_HISTORY,其中有3列:SUBSCRIBER_IDSTART_DATEEND_DATE。设唯一订阅者数为N

我想创建一个包含3列的新表:

  1. SUBSCRIBER_ID:所有唯一用户ID连续重复36次。
  2. MONTHLY_CALENDAR_ID:对于每个SUBSCRIBER_ID,此列的日期为2015年7月至2018年7月(36个月)。
  3. ACTIVE:此列将用作每个订阅者以及该月是否有订阅的标志。此订阅数据在名为UPCALL_HISTORY的表中。

我对SQL还是很陌生,没有很多经验。我擅长Python,但是SQL似乎不像Python。

任何可以帮助我建立此表的查询想法吗?

让我的UPCALL_HISTORY表为:

+---------------+------------+------------+
| SUBSCRIBER_ID | START_DATE |  END_DATE  |
+---------------+------------+------------+
|           119 | 01/07/2015 | 01/08/2015 |
|           120 | 01/08/2015 | 01/09/2015 |
|           121 | 01/09/2015 | 01/10/2015 |
+---------------+------------+------------+

我想要一个看起来像这样的表

+---------------+------------+--------+
| SUBSCRIBER_ID |   MON_CA   | ACTIVE |
+---------------+------------+--------+
| 119           | 01/07/2015 |      1 |
| *             | 01/08/2015 |      0 |
| *             | 01/09/2015 |      0 |
| (36 times)    | 01/10/2015 |      0 |
| *             | *          |      0 |
| 119           | 01/07/2018 |      0 |
+---------------+------------+--------+

持续120121

编辑:添加了示例

4 个答案:

答案 0 :(得分:3)

这就是我对这个问题的理解。

示例表和几行:

SQL> create table upcall_history
  2    (subscriber_id number,
  3     start_date    date,
  4     end_date      date);

Table created.

SQL> insert into upcall_history
  2    select 1, date '2015-12-25', date '2016-01-13' from dual union
  3    select 1, date '2017-07-10', date '2017-07-11' from dual union
  4    select 2, date '2018-01-01', date '2018-04-24' from dual;

3 rows created.

创建一个新表。对于不同的SUBSCRIBER_ID's,它会创建36个“月”行,固定行(如您所述)。

SQL> create table new_table as
  2    select
  3      x.subscriber_id,
  4      add_months(date '2015-07-01', column_value - 1) monthly_calendar_id,
  5      0 active
  6    from (select distinct subscriber_id from upcall_history) x,
  7         table(cast(multiset(select level from dual
  8                             connect by level <= 36
  9                            ) as sys.odcinumberlist));

Table created.

ACTIVE表的MONTHLY_CALENDAR_IDSTART_DATE中包含END_DATE的行的UPCALL_HISTORY列值更新为“ 1”。

SQL> merge into new_table n
  2    using (select subscriber_id, start_date, end_date from upcall_history) x
  3    on (    n.subscriber_id = x.subscriber_id
  4        and n.monthly_calendar_id between trunc(x.start_date, 'mm')
  5                                      and trunc(x.end_date, 'mm')
  6       )
  7  when matched then
  8    update set n.active = 1;

7 rows merged.

SQL>

结果(仅ACTIVE = 1):

SQL> select * from new_table
  2  where active = 1
  3  order by subscriber_id, monthly_calendar_id;

SUBSCRIBER_ID MONTHLY_CA     ACTIVE
------------- ---------- ----------
            1 2015-12-01          1
            1 2016-01-01          1
            1 2017-07-01          1
            2 2018-01-01          1
            2 2018-02-01          1
            2 2018-03-01          1
            2 2018-04-01          1

7 rows selected.

SQL>

答案 1 :(得分:2)

如果您使用的是12c,则可以对cross apply使用所有月份的内联视图,以获取具有所有ID的月份的组合:

select uh.subscriber_id, m.month,
  case when trunc(uh.start_date, 'MM') <= m.month
      and (uh.end_date is null or uh.end_date >= add_months(m.month, 1))
    then 1 else 0 end as active
from upcall_history uh
cross apply (
  select add_months(trunc(sysdate, 'MM'), - level) as month
  from dual
  connect by level <= 36
) m
order by uh.subscriber_id, m.month;

我已经将其设置为到当前月份为止的36个月的滚动窗口,但实际上您可能希望像问题中那样确定日期。

包含来自CTE的示例数据:

with upcall_history (subscriber_id, start_date, end_date) as (
  select 1, date '2015-09-04', '2015-12-15' from dual
  union all select 2, date '2017-12-04', '2018-05-15' from dual
)

生成72行:

SUBSCRIBER_ID MONTH          ACTIVE
------------- ---------- ----------
            1 2015-07-01          0
            1 2015-08-01          0
            1 2015-09-01          1
            1 2015-10-01          1
            1 2015-11-01          1
            1 2015-12-01          0
            1 2016-01-01          0
...
            2 2017-11-01          0
            2 2017-12-01          1
            2 2018-01-01          1
            2 2018-02-01          1
            2 2018-03-01          1
            2 2018-04-01          1
            2 2018-05-01          0
            2 2018-06-01          0

您可以使用它来创建新表或填充现有表;但是,如果您要做想要滚动窗口,则视图可能更合适。


如果您未使用12c,则cross apply不可用-您会收到“ ORA-00905:缺少关键字”。

您可以通过交叉连接两个CTE(一个获取所有月份,另一个获取所有ID)获得相同的结果,然后外部连接到您的实际数据:

with m (month) as (
  select add_months(trunc(sysdate, 'MM'), - level)
  from dual
  connect by level <= 36
),
i (subscriber_id) as (
  select distinct subscriber_id
  from upcall_history
)
select i.subscriber_id, m.month,
  case when uh.subscriber_id is null then 0 else 1 end as active
from m
cross join i
left join upcall_history uh
on uh.subscriber_id = i.subscriber_id
and trunc(uh.start_date, 'MM') <= m.month
and (uh.end_date is null or uh.end_date >= add_months(m.month, 1))
order by i.subscriber_id, m.month;

答案 2 :(得分:2)

您可以使用分区外部联接在11g中做到这一点,就像这样:

WITH upcall_history AS (SELECT 119 subscriber_id, to_date('01/07/2015', 'dd/mm/yyyy') start_date, to_date('01/08/2015', 'dd/mm/yyyy') end_date FROM dual UNION ALL
                        SELECT 120 subscriber_id, to_date('01/08/2015', 'dd/mm/yyyy') start_date, to_date('01/09/2015', 'dd/mm/yyyy') end_date FROM dual UNION ALL
                        SELECT 121 subscriber_id, to_date('01/09/2015', 'dd/mm/yyyy') start_date, to_date('01/10/2015', 'dd/mm/yyyy') end_date FROM dual),
              mnths AS (SELECT add_months(TRUNC(SYSDATE, 'mm'), + 1 - LEVEL) mnth
                        FROM   dual
                        CONNECT BY LEVEL <= 12 * 3 + 1)
SELECT uh.subscriber_id,
       m.mnth,
       CASE WHEN mnth BETWEEN start_date AND end_date - 1 THEN 1 ELSE 0 END active
FROM   mnths m
       LEFT OUTER JOIN upcall_history uh PARTITION BY (uh.subscriber_id) ON (1=1)
ORDER BY uh.subscriber_id,
         m.mnth;

SUBSCRIBER_ID MNTH            ACTIVE
------------- ----------- ----------
          119 01/07/2015           1
          119 01/08/2015           0
          119 01/09/2015           0
          119 01/10/2015           0
          <snip>
          119 01/06/2018           0
          119 01/07/2018           0
          --
          120 01/07/2015           0
          120 01/08/2015           1
          120 01/09/2015           0
          120 01/10/2015           0
          <snip>
          120 01/06/2018           0
          120 01/07/2018           0
          --
          121 01/07/2015           0
          121 01/08/2015           0
          121 01/09/2015           1
          121 01/10/2015           0
          <snip>
          121 01/06/2018           0
          121 01/07/2018           0

我已经假设了一些有关您的开始/结束日期以及有效期的信息;希望您可以轻松地调整case语句以适合最适合您情况的逻辑。

答案 3 :(得分:0)

我也相信这可以作为CROSS JOIN的示例。我要做的就是创建一个包含所有日期的小表,然后将CROSS JOIN与订户表一起使用。

示例:https://www.essentialsql.com/cross-join-introduction/

相关问题