加入到每行单独创建的整数范围

时间:2013-02-17 15:22:50

标签: sql oracle select join range

从表格中可以看出:

|   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

2 个答案:

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

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