【问题标题】:how can I fetch even or odd records from a table?如何从表中获取偶数或奇数记录?
【发布时间】:2015-04-21 04:26:13
【问题描述】:

我尝试使用以下两个查询显示奇数或偶数记录。

select * from employee a 
    where 1 = mod(rownum,2);
select * from employee 
   where (rowid,1) in (select rowid, mod(rownum,2) 
                       from employee);

第一个仅显示员工表的第一条记录。而第二个查询完美无缺。请问,谁能解释一下rownum是如何工作的??

【问题讨论】:

  • 您应该注意,表中的行必须被视为没有顺序,除非由数据定义,因此您需要一个 ORDER BY 子句才能有意义。

标签: sql oracle rownum


【解决方案1】:

这是因为rownum 的特殊性。这是一个pseudo-column,应用于查询返回的结果集。 (这就是为什么WHERE ROWNUM > 1 总是假的。)

在这种情况下,使用 ROWNUM 会导致查询在 WHERE 子句返回 false 时停止。所以这不会返回任何行(因为 0 只为偶数行返回):

select * from employee a 
    where 0 = mod(rownum,2); 

您的第二种方法有一个子查询,它不在 WHERE 子句中使用 ROWNUM,因此允许返回整个表以在外部查询中进行评估。

任何允许在不评估 ROWNUM 的情况下实现整个结果集的方法都可以使用。这也会产生你想要的结果:

select * from
    (select a.*, rownum as rn from employee a)
where mod(rn,2) = 1
/

正如@DavidAldridge 在他的评论中指出的那样,如果没有 ORDER BY 子句,结果集基本上是随机的。 ROWNUM 不能与 ORDER BY 配合使用,因此要保证排序,请改用分析函数 ROW_NUMBER()。

select * from
    (select a.*
            , row_number() over (order by a.emp_id)  as rn 
     from employee a)
where mod(rn,2) = 0
/

" 下面的查询如何只从表中获取前两条记录。"

通过COUNT STOPKEY 操作的奇迹。查询知道需要多少行;它返回行(并分配 ROWNUM 的值),直到达到该限制。

我们可以在解释计划中看到这一点。没有过滤器:

SQL> explain plan for 
  2      select * from emp;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     3 |   111 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| EMP  |     3 |   111 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)

12 rows selected.

SQL> 

这是where rownum <= 2 的计划。注意所选行的不同:

SQL> explain plan for 
  2      select * from emp
  3      where rownum <= 2;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1973284518

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     2 |    74 |     3   (0)| 00:00:01 |
|*  1 |  COUNT STOPKEY     |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL| EMP  |     3 |   111 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=2)

Note
-----
   - dynamic sampling used for this statement (level=2)

18 rows selected.

SQL> 

【讨论】:

  • 谢谢你。对此还有一个疑问。正如你所说,rownum 在最后进行评估。即,一旦查询返回结果集,就会添加 rownum。那么下面的查询如何从表中只获取前两条记录。查询是 select * from employee where rownum
【解决方案2】:

您的问题是rownum 的工作原理。

rownum 不像表中的其他列;它是一个伪列。它仅对最终结果集中的行增加。因此,获得第二行的著名示例是:

where rownum = 2

这个条件永远不会满足,因为rownum 设置为 1 用于第一行输出。因此,第一行得到处理。 rownum 是 1。它失败了 where。处理完第二行,rownum 仍然是 1(因为结果集还是空的),它失败了 where。以此类推。

最安全的解决方案是使用row_number():

select *
from (select a.*, row_number() over (order by rowid) as seqnum
      from employee a 
     ) a
where mod(seqnum, 2) = 1;

这符合您的预期。

说实话,以下内容也是如此:

select *
from (select a.*, rownum as seqnum
      from employee a 
     ) a
where mod(seqnum, 2) = 1;

子查询中没有过滤,行号是按你期望的顺序分配的。

【讨论】:

  • +1 但您可能会说,“rownum 设置为 1 用于标识为所需结果集的一部分的第一行”。
  • @DavidAldridge 。 . .我觉得你说得很好。
  • 谢谢。如果最终结果集中应用了rownum,那么rownum
  • @Yugamani 。 . .每次在结果集中放置一行时,rownum 就会递增。在rownum 上运行的where 部分仍然适用。
猜你喜欢
  • 1970-01-01
  • 2020-06-24
  • 2021-08-01
  • 1970-01-01
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
  • 2017-02-06
相关资源
最近更新 更多