滞后并导致下个月

时间:2018-09-11 21:46:22

标签: oracle oracle12c

TABLE: HIST
CUSTOMER   MONTH   PLAN
1          1       A
1          2       B
1          2       C
1          3       D

如果我查询:

select h.*, lead(plan) over (partition by customer order by month) np from HIST h

我得到:

CUSTOMER   MONTH   PLAN   np
1          1       A      B
1          2       B      C
1          2       C      D
1          3       D      (null)

但是我想要

CUSTOMER   MONTH   PLAN   np
1          1       A      B
1          2       B      D
1          2       C      D
1          3       D      (null)

下个月23的原因是D。我猜partition by customer order by month不能按照我的想法工作。

在Oracle 12c中是否可以实现这一目标?

3 个答案:

答案 0 :(得分:3)

一种实现方法是将RANGE分区与MIN分析函数一起使用。像这样:

select h.*, 
       min(plan) over 
           (partition by customer 
            order by month 
            range between 1 following and 1 following) np 
from HIST h;
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
|        1 |     1 | A    | B  |
|        1 |     2 | B    | D  |
|        1 |     2 | C    | D  |
|        1 |     3 | D    |    |
+----------+-------+------+----+

使用RANGE分区时,是在告诉Oracle根据要排序的列的 values 来创建窗口,而不是根据行来创建窗口。 / p>

例如,

ROWS BETWEEN 1 following and 1 following

...将创建一个包含下一行的窗口。

RANGE BETWEEN 1 following and 1 following

...将创建一个窗口,其中包含具有month下一个值的所有行。

更新

如果对于给定的客户,可能会跳过MONTH的某些值,则可以使用此变体:

select h.*, 
       first_value(plan) over 
          (partition by customer 
           order by month 
           range between 1 following and unbounded following) np 
from h 
+----------+-------+------+----+
| CUSTOMER | MONTH | PLAN | NP |
+----------+-------+------+----+
|        1 |     1 | A    | B  |
|        1 |     3 | B    | D  |
|        1 |     3 | C    | D  |
|        1 |     4 | D    |    |
+----------+-------+------+----+

答案 1 :(得分:2)

您可以使用LAG / LEAD两次。第一次检查重复的月份,并将这些月份的值设置为NULL,第二次使用IGNORE NULLS获取下一个每月的值。

它的另一个好处是,如果跳过了几个月,它仍然会找到下一个值。

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE HIST ( CUSTOMER, MONTH, PLAN ) AS
SELECT 1, 1, 'A' FROM DUAL UNION ALL
SELECT 1, 2, 'B' FROM DUAL UNION ALL
SELECT 1, 2, 'C' FROM DUAL UNION ALL
SELECT 1, 3, 'D' FROM DUAL UNION ALL
SELECT 2, 1, 'E' FROM DUAL UNION ALL
SELECT 2, 1, 'F' FROM DUAL UNION ALL
SELECT 2, 3, 'G' FROM DUAL UNION ALL
SELECT 2, 5, 'H' FROM DUAL;

查询1

SELECT CUSTOMER,
       MONTH,
       PLAN,
       LEAD( np ) IGNORE NULLS OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM ) AS np
FROM   (
  SELECT h.*,
         CASE MONTH
         WHEN LAG( MONTH ) OVER ( PARTITION BY CUSTOMER ORDER BY MONTH, PLAN, ROWNUM )
         THEN NULL
         ELSE PLAN
         END AS np
  FROM   hist h
)

Results

| CUSTOMER | MONTH | PLAN |     NP |
|----------|-------|------|--------|
|        1 |     1 |    A |      B |
|        1 |     2 |    B |      D |
|        1 |     2 |    C |      D |
|        1 |     3 |    D | (null) |
|        2 |     1 |    E |      G |
|        2 |     1 |    F |      G |
|        2 |     3 |    G |      H |
|        2 |     5 |    H | (null) |

答案 2 :(得分:2)

仅在此处将它作为Oracle 12c(向前)的选项列出,您可以使用Apply运算符来解决此类问题

select
     h.customer, h.month, h.plan, oa.np
from hist h
outer apply (
  select
       h2.plan as np
  from hist h2
  where h.customer = h.customer
  and h2.month > h.month
  order by month
  fetch first 1 rows only
  ) oa
order by
     h.customer, h.month, h.plan

我不知道任何Oracle 12c公共玩弄,因此,可以在这里找到SQL Server中的示例:http://sqlfiddle.com/#!18/cd95e/1

| customer | month | plan |     np |
|----------|-------|------|--------|
|        1 |     1 |    A |      C |
|        1 |     2 |    B |      D |
|        1 |     2 |    C |      D |
|        1 |     3 |    D | (null) |