2017-04-11 111 views
0

我正在按照评论者的建议改写我以前的问题。查找无序列表中最长的有序列表

下面的集合表示产品连续几周的销售额。

22,19,20,23,16,14,15,15,18,21,24,10,17 

我需要找到更高的销售数字在连续两周的最长的,即周6周11 14,15,15,18,21,24表示。

我有一个查询返回结果,但想知道如何改进它。任何人都可以建议一些方法来做到这一点?提前致谢。

with 
raw_data (sales) as 
(
    select '22,19,20,23,16,14,15,15,18,21,24,10,17' from dual 
) 
, 
derived_tbl(week, sales) as 
(
    select level, regexp_substr(sales, '([[:digit:]]+)(,|$)', 1, level, null, 1) 
    from raw_data connect by level <= regexp_count(sales,',')+1 
) 
, 
coll(week, sales, salesdlag, salesdlead) as 
(
    select week, sales, 
    nvl(sales - (lag(sales) over (order by week)), 0), 
    nvl((lead(sales) over (order by week) - sales), 0) 
    from derived_tbl 
) 
, 
filt_coll(week, sales, salesdlag, salesdlead) as 
(
    select week, sales, salesdlag, salesdlead 
    from coll 
    where not (salesdlag < 0 and salesdlead < 0) 
) 
, 
cte(startweek, sales, salesdlag, salesdlead, actualweek) as 
(
    select week, sales, salesdlag, salesdlead, week from filt_coll 
    union all 
    select cte.startweek, cl.sales, cl.salesdlag, cl.salesdlead, cl.week 
    from filt_coll cl, cte 
    where cl.week = cte.actualweek + 1 and cl.sales >= cte.sales 
) 
, 
pen_coll as 
(
    select * from cte order by startweek,actualweek 
) 
, 
final_coll as 
(
    select startweek, actualweek, sales, count(startweek) over(PARTITION BY startweek) as cnt from pen_coll 
) 
select LISTAGG(sales, ',') within group (order by null) as rslt 
from final_coll 
where cnt = (select max(cnt) from final_coll) 
; 
+0

你为什么把它作为一个逗号分隔的字符串,而不是在[收藏](HTTPS ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm#LNPLS005)? – MT0

回答

0

使用集合(而不是分隔的字符串)来存储的值,你也可以使用一个分层查询找到了结果:

SELECT * 
FROM (
    SELECT TRIM(LEADING ',' FROM SYS_CONNECT_BY_PATH(value, ',')) AS week_values, 
     CONNECT_BY_ROOT id AS first_week, 
     id AS last_week, 
     LEVEL AS num_weeks, 
     MAX(LEVEL) OVER() AS max_weeks 
    FROM (
    SELECT COLUMN_VALUE AS value, 
      ROWNUM As id 
    FROM TABLE(SYS.ODCINUMBERLIST(22,19,20,23,16,14,15,15,18,21,24,10,17)) 
) 
    WHERE CONNECT_BY_ISLEAF = 1 
    CONNECT BY PRIOR id + 1 = id AND PRIOR value <= value 
) 
WHERE num_weeks = max_weeks 

输出

WEEK_VALUES  FIRST_WEEK LAST_WEEK NUM_WEEKS MAX_WEEKS 
----------------- ---------- --------- --------- --------- 
14,15,15,18,21,24   6  11   6   6 

您可以进一步限制分层查询仅在先前值较大时才开始:

SELECT * 
FROM (
    SELECT TRIM(LEADING ',' FROM SYS_CONNECT_BY_PATH(value, ',')) AS week_values, 
     CONNECT_BY_ROOT id AS first_week, 
     id AS last_week, 
     LEVEL AS num_weeks, 
     MAX(LEVEL) OVER() AS max_weeks 
    FROM (
    SELECT COLUMN_VALUE AS value, 
      LAG(COLUMN_VALUE) OVER (ORDER BY ROWNUM) AS prev_value, 
      ROWNUM As id 
    FROM TABLE(SYS.ODCINUMBERLIST(22,19,20,23,16,14,15,15,18,21,24,10,17)) 
) 
    WHERE CONNECT_BY_ISLEAF = 1 
    START WITH prev_value IS NULL OR prev_value > value 
    CONNECT BY PRIOR id + 1 = id AND PRIOR value <= value 
) 
WHERE num_weeks = max_weeks 

更新

甲骨文设置:

create table tst(id, value) as 
    select rownum, round(dbms_random.value*9) 
    from dual 
    connect by level <= 10000; 

查询:

SELECT * 
FROM (
    SELECT TRIM(LEADING ',' FROM SYS_CONNECT_BY_PATH(value, ',')) AS week_values, 
     CONNECT_BY_ROOT id AS first_week, 
     id AS last_week, 
     LEVEL AS num_weeks, 
     MAX(LEVEL) OVER() AS max_weeks 
    FROM (
    SELECT value, 
      LAG(value) OVER (ORDER BY id) AS prev_value, 
      id 
    FROM tst 
) 
    WHERE CONNECT_BY_ISLEAF = 1 
    START WITH prev_value IS NULL OR prev_value > value 
    CONNECT BY PRIOR id + 1 = id AND PRIOR value <= value 
) 
WHERE num_weeks = max_weeks 
+0

我刚刚开始学习超出基本的选择语句。对收藏品一无所知。你给了我一些学习指针。非常感谢。 – Slkrasnodar

+0

尝试用'create table tst(id,value)'的结果集替换集合(选择rownum,round(dbms_random.value * 9)from double connect by level <= 10000);'table。似乎并不喜欢它... – Slkrasnodar

+0

@Slkrasnodar添加了一个我希望能够工作的更新(尽管目前无法测试)。 – MT0