【问题标题】:Join to range of integers, which is created separately for each row加入整数范围,为每一行单独创建
【发布时间】:2013-02-02 01:49:55
【问题描述】:

从表中:

|   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

【问题讨论】:

    标签: sql oracle select join range


    【解决方案1】:

    在 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
    

    【讨论】:

      【解决方案2】:

      递归(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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-04
        • 1970-01-01
        • 2020-12-18
        • 1970-01-01
        • 1970-01-01
        • 2018-10-31
        • 2017-06-04
        • 1970-01-01
        相关资源
        最近更新 更多