在无序列表中查找最长的有序列表

时间:2017-04-11 09:34:43

标签: sql oracle

我正在按照评论者的建议重新提出我之前提出的问题。

以下数据表示产品连续几周的销售情况。

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

我需要连续几周找到最长的销售数字,即由14,15,15,18,21,24代表的第6周到第11周。

我有一个返回结果的查询,但想知道改进它的方法。有人可以建议一些方法吗?提前谢谢。

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)
;

1 个答案:

答案 0 :(得分: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

<强>更新

Oracle安装程序:

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