【问题标题】:oracle 11g sql query obtains every number from set of rangesoracle 11g sql查询从一组范围内获取每个数字
【发布时间】:2018-07-18 11:27:45
【问题描述】:

请帮帮我! 我有一个表 T_RANGE 有两列 A 和 B (都是整数数据类型)。 该表的每一行表示一个整数范围。 以下是测试数据:

A | B
______
10 | 12
23 | 25
51 | 55

我必须进行一个 SQL 查询(Oracle 11g)从 T_RANGE 表的所有范围中获取每个整数(它必须是结果集中的一个整数列)。 对于我上面描述的测试数据,结果应该是

10
11
12
23
24
25
51
52
53
54
55

【问题讨论】:

    标签: sql oracle integer range


    【解决方案1】:

    SQL Fiddle

    Oracle 11g R2 架构设置:

    CREATE TABLE table_name ( A, B ) AS
    SELECT 10, 12 FROM DUAL UNION ALL
    SELECT 23, 25 FROM DUAL UNION ALL
    SELECT 51, 55 FROM DUAL;
    

    查询 1 使用递归子查询因式分解子句(又名 CTE):

    WITH cte ( a, b ) AS (
      SELECT A, B FROM table_name
    UNION ALL
      SELECT A + 1, B FROM cte WHERE A < B
    )
    SELECT a FROM cte
    

    Results:

    |  A |
    |----|
    | 10 |
    | 23 |
    | 51 |
    | 11 |
    | 24 |
    | 52 |
    | 12 |
    | 25 |
    | 53 |
    | 54 |
    | 55 |
    

    查询 2 使用相关的分层查询:

    SELECT n.COLUMN_VALUE
    FROM   table_name t
           CROSS JOIN
           TABLE(
             CAST(
               MULTISET(
                 SELECT t.A + LEVEL - 1
                 FROM   DUAL
                 CONNECT BY t.A + LEVEL - 1 <= t.B
               ) AS SYS.ODCINUMBERLIST
             )
           ) n
    

    Results:

    | COLUMN_VALUE |
    |--------------|
    |           10 |
    |           11 |
    |           12 |
    |           23 |
    |           24 |
    |           25 |
    |           51 |
    |           52 |
    |           53 |
    |           54 |
    |           55 |
    

    【讨论】:

      【解决方案2】:

      这个分层查询对我有用:

      select a + level - 1 num
        from (select rownum rn, a, b from t_range) 
        connect by a + level -1 <= b and prior rn = rn and prior sys_guid() is not null
      

      SQLFiddle demo

      【讨论】:

      • 由于PRIOR SYS_GUID() IS NOT NULL 始终为真,因此您应该能够删除该子句并期望查询能够正常工作 - 但是,如果没有那个未记录的黑魔法(或其他类似的黑客),查询将不起作用正如预期的那样。
      • @MTO = 有一个解释 on this Oracle Community thread。我通常使用dbms_random.value 而不是sys_guid(),但我相信它可以是任何非确定性函数..
      【解决方案3】:

      使用 XML DB 功能生成数字范围的另一种方法:

      -- CTE for your sample data
      with t_range(a, b) as (
        select 10, 12 from dual
        union all select 23, 25 from dual
        union all select 51, 55 from dual
      )
      -- actual query
      select x.n
      from t_range t
      cross join xmltable(
        'xs:integer($a) to xs:integer($b)'
        passing t.a as "a", t.b as "b"
        columns n number path '.'
      ) x;
      
               N
      ----------
              10
              11
              12
              23
              24
              25
              51
              52
              53
              54
              55
      
      11 rows selected. 
      

      这通过传入下限和上限(以小数形式表示,因此必须转换为整数)提供了一个 XPath 序列,并将序列值作为名为 n 的数字列返回。

      与往常一样,当有多种方法时,请将它们的性能与实际数据进行比较。除此之外,实际范围的大小会影响它们的运行速度、消耗的资源,甚至在极端情况下它们是否会运行。

      【讨论】:

        【解决方案4】:

        一个选项是“日历表”方法,我们创建一个包含连续序列的表,其宽度足以覆盖表中的所有值。然后,我们可以将此序列表连接到您的数据表并生成范围:

        WITH numbers AS ( 
            SELECT 0 AS Number 
            UNION ALL 
            SELECT Number + 1 
            FROM Numbers 
            WHERE Number + 1 <= 100
        ),
        yourTable AS (
            SELECT 10 AS col1, 12 AS col2 UNION ALL
            SELECT 23, 25 UNION ALL
            SELECT 51, 55
        )
        
        SELECT *
        FROM numbers t1
        INNER JOIN yourTable t2
            ON t1.Number BETWEEN t2.col1 AND t2.col2
        ORDER BY
            t1.Number;
        

        对上述内容的改进可能是对 CTE 序列的边界使用动态数据。但是,如果您在生产环境中执行此操作,您可能只是硬编码一个足够大的范围以涵盖任何预期值。或者,您可以创建一个包含完整序列的单独真实表。

        【讨论】:

        • 如果范围大于您的幻数 (100),这将失败。
        • @MT0 我对此提出了警告。在生产中,如果 OP 使用这种方法(我认为表格会更好),我假设他们会提前知道范围的范围。
        【解决方案5】:

        Oracle 最佳实践是通过连接来生成数字。 此外,数字是不应使用的保留字。

        所以 Tim 的 SQL 在 Oracle 中:

        WITH numbers AS ( 
            SELECT level nb     FROM dual connect by level <= 100
        ),
        yourTable AS (
            SELECT 10 AS col1, 12 AS col2 from dual UNION ALL
            SELECT 23, 25 from dual   UNION ALL
            SELECT 51, 55 from dual  
        )
        SELECT nb
        FROM numbers t1, yourTable t2
        where t1.nb BETWEEN t2.col1 AND t2.col2
        ORDER BY t1.nb;
        

        【讨论】:

        • 如果范围大于您的幻数 (100),这将失败。
        • 为什么使用 connect-by 而不是递归 CTE 的“最佳实践”?
        • 因为生成少量(1000000)条记录会更快。对于 1.000.000 行 0,5s 与 7s。对于这个例子,如果你从一个大数字开始并且有一个小范围的递归子查询分解可能会更好
        猜你喜欢
        • 2012-04-22
        • 2012-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多