SQL查询从双重返回N行

Posted

技术标签:

【中文标题】SQL查询从双重返回N行【英文标题】:SQL Query to return N rows from dual 【发布时间】: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 上是正确的 【参考方案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 是否会接受来自绑定变量的限制。

参考:

Integer Series Generators - CONNECT BY LEVEL Method

【讨论】:

+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 提供解决方案。

以上是关于SQL查询从双重返回N行的主要内容,如果未能解决你的问题,请参考以下文章

如何双重加入sql查询?

ORACLE SQL 查询从其他表中的行字符串匹配的行返回值

查找缺失行的 SQL 查询返回语法错误

Union链接查询

Java JDBC 对要求仅从特定行返回详细信息的 SQL 查询抛出错误

如何跳过sql查询中的前n行