【问题标题】:SQL Query to return N rows from dualSQL查询从双重返回N行
【发布时间】:2009-12-29 09:14:19
【问题描述】:

我想编写一个接受绑定变量(比如:NUM)的 SQL 查询,它的输出由一列和:NUM 行组成,每行都有其行号。即,如果我们将 :NUM 作为 7 传递,则输出应为:

VAL
====
1
2
3
4
5
6
7

查询中不应有任何实际的数据库表,也不应使用 PL/SQL 代码。即在查询中只应使用对偶

有什么办法可以做到吗?

【问题讨论】:

  • 重新标记为 oracle,希望是正确的
  • @Rob:Postgres 也支持 PLSQL,但不支持FROM DUAL 所以Oracle 是正确的。
  • 谢谢 Rob,这个问题只在 Oracle DB 上是正确的

标签: sql oracle plsql


【解决方案1】:

你可以使用:

 WHERE ROWNUM <= :NUM

...但是表必须包含等于或大于绑定变量限制的行。 This link demonstrates various row number generation techniques in Oracle.

使用CONNECT BY,Oracle 10g+:

SELECT LEVEL
  FROM DUAL
CONNECT BY LEVEL <= :NUM

monojohnny 确认可以使用绑定变量。尝试在 Oracle 9i 上运行,尽管支持 CONNECT BY 语法会导致 ORA-01436 错误。

我唯一没有 100% 了解的是 CONNECT BY 是否会接受来自绑定变量的限制。

参考:

【讨论】:

  • +1 这里也推荐这个方法:adp-gmbh.ch/ora/sql/examples/generate_rows.html
  • 这会给我一个高于 1 的值的错误。 SELECT LEVEL FROM DUAL CONNECT BY LEVEL &lt;= 20 : ORA-01436
  • 好点,它可以在较新的服务器(10g)上运行,但不能在 9.0.1.5.0 上运行。不过,您链接中的其他选项效果很好。
  • 关于这个使用绑定变量:我试过了,它确实有效(使用 10.2.0.1.0):我会发布这个效果的答案以供参考。
【解决方案2】:

尝试类似:

SELECT 1 AS Val FROM dual
UNION ALL SELECT 2 FROM dual
UNION ALL SELECT 3 FROM dual
UNION ALL SELECT 4 FROM dual
UNION ALL SELECT 5 FROM dual
UNION ALL SELECT 6 FROM dual
UNION ALL SELECT 7 FROM dual;

虽然很麻烦,但可以解决问题。

已编辑:啊 - 你需要传入一个变量来让你知道要走多高...

那么怎么样:

SELECT t1.Val + t2.Val * 2 + t3.Val * 4 + t4.Val * 8 AS Val
FROM
(
SELECT 0 AS Val FROM dual
UNION ALL SELECT 1 FROM dual
) AS t1, 
(
SELECT 0 AS Val FROM dual
UNION ALL SELECT 1 FROM dual
) AS t2, 
(
SELECT 0 AS Val FROM dual
UNION ALL SELECT 1 FROM dual
) AS t3, 
(
SELECT 0 AS Val FROM dual
UNION ALL SELECT 1 FROM dual
) AS t4
WHERE t1.Val + t2.Val * 2 + t3.Val * 4 + t4.Val * 8 <= 7;

好的...再次编辑,现在使用 WITH:

WiTH 
A0 AS (SELECT 0 as N FROM DUAL UNION ALL SELECT 0 FROM DUAL),
A1 AS (SELECT 0 as N FROM A0, A0 AS B),
A2 AS (SELECT 0 as N FROM A1, A1 AS B),
A3 AS (SELECT 0 as N FROM A2, A2 AS B),
A4 AS (SELECT 0 as N FROM A3, A3 AS B),
A5 AS (SELECT 0 as N FROM A4, A4 AS B),
A6 AS (SELECT 0 as N FROM A5, A5 AS B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY N) AS Val FROM A6)
SELECT *
FROM Nums
WHERE Val <= :NUM
;

【讨论】:

  • ...然后根据需要继续该模式。您可以使用递归 CTE,但我不确定 Oracle 语法是什么。您也可以使用 row_number 样式的方法。
  • @Rob:Oracle 对递归 WITH 子句的支持从 11g iirc 开始。
  • 是的,但我不确定它是否与 MS-SQL 中的相同。
【解决方案3】:

我没有想出这个答案 [所以请确保任何投票都以正确的方式进行!],这只是我基于“OMG Ponies”的测试笔记[不确定该方法是否适用于绑定变量]以上供参考:

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> var num_rows number
SQL> begin select 20 into :num_rows from dual;
  2  end;
  3  /

PL/SQL procedure successfully completed.

SQL> select level from dual
  2  connect by level <=:num_rows;

     LEVEL
----------
         1
         2
         3
         4
 ...

【讨论】:

    【解决方案4】:

    不通过连接查询

    WITH num(n) as(select 1 from dual union all
    select n+1 from num where n <= :num_limit)
    select * from num
    

    【讨论】:

      【解决方案5】:

      我正在标记这个社区 wiki,因为它实际上并没有满足您对没有表的要求,但是我们在安装数据库时做的第一件事就是为此目的创建一组表。

      • 包含大量整数(例如,-99999 到 99999)的表。
      • 包含从过去 10 年到未来 10 年的每个日期的表格(每个月不断添加并偶尔修剪)。
      • 包含一天中每个小时的表格。

      通过这样做,我们以(最小且便宜的)磁盘空间为代价,大大降低了大量查询的复杂性并提高了速度。

      你应该认真考虑一下。除了维护日期表之外,不需要太多维护。

      【讨论】:

      • DUAL 是执行这种事情的最佳方式——在 10g+ 中,Oracle 提供了涉及零块读取的 FAST DUAL。 DUAL 几乎总能胜过自制桌子。
      • 毫无疑问,但我们不仅仅使用 Oracle。我们拥有的解决方案与供应商无关,并且执行速度足够快。
      【解决方案6】:

      另一种解决方案需要一些 PL/SQL 来创建一个函数来返回包含行的集合...不像 select level from dual connect by level &lt;= :b1 方法那么简单,但它在一些情况下很有用:

      1) 创建一个数字表对象类型(本例中为 number_tbl):

      create or replace type number_tbl as table of number;
      

      2) 创建一个函数,该函数将接收要生成的行数,然后返回带有结果的 number_tbl 对象:

      create or replace function get_rows( i_num_rows number ) return number_tbl as
        t number_tbl := number_tbl();
      begin
        if i_num_rows < 1 then
          return null;
        end if;
      
        t.extend( i_num_rows );
      
        for i in 1..i_num_rows loop
          t(i) := i;
        end loop;
      
        return t;
      end get_rows;
      

      3) 使用 table( ... ) 函数从您的函数中选择,将您的 number_tbl 对象转换为可选择的对象:

      select * from table( cast ( get_rows( :b1 ) as number_tbl ) );
      

      【讨论】:

        【解决方案7】:

        connect by 真是太棒了。它可以帮助您使用双表中可用的一组数据生成多行。这可以帮助您为虚拟数据生成大量行。例如

        insert into test select a.* from test1 a,(select * from dual connect by level <=100000) b;
        

        或者你可以这样做

        示例 2:您要打印 1 到 10 的正方形和立方体。

        SQL> select level "No", power(level,2) "Square", power(level,3) "Cube"  from dual     connect by level <= 10;
        
            No     Square       Cube
        ---------- ---------- ----------
             1          1          1
             2          4          8
             3          9         27
             4         16         64
             5         25        125
             6         36        216
             7         49        343
             8         64        512
             9         81        729
            10        100       1000
        

        因此,您可以以任何您想要的形式对其进行操作。这就是您可以从双表返回多行的方式。 参考资料:http://www.oraclebin.com/2012/12/multipe-rows-from-dual-table.html

        【讨论】:

          【解决方案8】:

          另一种方法是使用 XQuery 范围表达式,例如:

          select column_value from xmltable(:a||' to '||:b);
          
           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
          

          这个解决方案非常灵活,例如:

          select column_value from xmltable('5 to 10, 15 to 20');
          
           5
           6
           7
           8
           9
          10
          15
          16
          17
          18
          19
          20
          

          【讨论】:

            【解决方案9】:
            WITH cte_numbers(n) 
            AS (
                SELECT 0
                UNION  ALL
                SELECT n + 1
                FROM  cte_numbers
                WHERE n < 10
            )
            SELECT n
            FROM cte_numbers;
            

            返回的行 0 1 2 3 4 5 6 7 8 9 10

            【讨论】:

              【解决方案10】:

              取决于数据库,可以使用各种方法。

              PostgreSQL 有一个不错的功能——series

              为了得到你想要的东西:

              SELECT * FROM generate_series(1, NUM);
              

              【讨论】:

              • 我认为从“PL/SQL”和“双”表的提及中,他希望为 Oracle 提供解决方案。
              猜你喜欢
              • 2012-10-09
              • 2018-05-07
              • 2019-05-23
              • 2013-09-12
              • 2021-06-01
              • 1970-01-01
              • 2013-10-01
              • 2017-12-01
              • 1970-01-01
              相关资源
              最近更新 更多