2

从表中:

|   name   |   range   |
------------------------
| 'Range1' | '456-458' |
| 'Range2' |   '11-13' |

只是想得到这个结果:

|   name   |   range   | value |
--------------------------------
| 'Range1' | '456-458' |  456  |
| 'Range1' | '456-458' |  457  |
| 'Range1' | '456-458' |  458  |
| 'Range2' |   '11-13' |   11  |
| 'Range2' |   '11-13' |   12  |
| 'Range2' |   '11-13' |   13  |

如果源表只有一个范围,则查询可以正常工作:

WITH data AS (
    SELECT 'Range1' name, '456-458' range FROM dual
)
SELECT ROWNUM, name, range, LEVEL value
FROM data, dual
WHERE LEVEL >= to_number(SUBSTR(range, 1, INSTR(range,'-')-1))
CONNECT BY LEVEL <= to_number(SUBSTR(range, INSTR(range,'-')+1));

但返回数万行,如果需要两个范围:

WITH data AS (
    SELECT 'Range1' name, '456-458' range FROM dual
    UNION
    SELECT 'Range2' name, '11-13' range FROM dual
)
SELECT ROWNUM, name, range, LEVEL value FROM data, dual
WHERE LEVEL >= to_number(SUBSTR(range, 1, INSTR(range,'-')-1))
CONNECT BY LEVEL <= to_number(SUBSTR(range, INSTR(range,'-')+1));

是否可以改进此查询以获得所需,或者我的方法最初是错误的?

Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
4

2 回答 2

2

在 11g 中,您可以对此类事物使用递归分解子查询。

SQL> with data as (select name, range,
  2                       to_number(substr(range, 1, instr(range,'-')-1)) from_val,
  3                       to_number(substr(range, instr(range,'-')+1)) to_val
  4                  from your_table),
  5  ranges (name, range, curr_val, from_val, to_val)
  6  as (select name, range, from_val, from_val, to_val
  7        from data
  8      union all
  9      select name, range, curr_val+1, from_val, to_val
 10        from ranges
 11       where curr_val < to_val)
 12  select name, range, curr_val
 13    from ranges
 14   order by name, curr_val;

NAME   RANGE     CURR_VAL
------ ------- ----------
Range1 456-458        456
Range1 456-458        457
Range1 456-458        458
Range2 11-13           11
Range2 11-13           12
Range2 11-13           13
于 2013-02-17T15:42:26.543 回答
1

递归(DazzaL 的答案)是一个更好的解决方案,但如果您想了解如何使用分层查询来实现它。

WITH data AS (
SELECT 'Range1' name
     , '456-460' range 
  FROM dual
UNION ALL
SELECT 'Range2' name
     , '11-13' range 
  FROM dual)

, ranges AS (
SELECT name
     , range
     , to_number(SUBSTR(range, 1, INSTR(range,'-')-1)) from_range
     , to_number(SUBSTR(range, INSTR(range,'-')+1)) to_range
  FROM data

), max_seq AS (
SELECT MAX(to_range - from_range) + 1 max_seq
  FROM ranges

), seq AS (
SELECT ROWNUM seq
  FROM max_seq
CONNECT BY LEVEL <= max_seq
) 

SELECT ROWNUM
     , name
     , range
     , from_range + seq - 1 value
  FROM ranges
 INNER JOIN seq
    ON seq <=  (to_range - from_range) + 1
于 2013-02-17T17:01:30.850 回答