【问题标题】:NTH row query in Oracle not behaving as expectedOracle 中的 NTH 行查询未按预期运行
【发布时间】:2016-11-03 21:30:45
【问题描述】:

给定的;

CREATE TABLE T1 (ID INTEGER, DESCRIPTION VARCHAR2(20));
INSERT INTO T1 VALUES (1,'ONE');
INSERT INTO T1 VALUES (2,'TWO');
INSERT INTO T1 VALUES (3,'THREE');
INSERT INTO T1 VALUES (4,'FOUR');
INSERT INTO T1 VALUES (5,'FIVE');
COMMIT;

为什么会这样;

SELECT * FROM 
( SELECT ROWNUM, ID, DESCRIPTION
  FROM T1)
WHERE MOD(ROWNUM,1)=0;  

返回

ROWNUM                                     ID DESCRIPTION        
------ -------------------------------------- --------------------
     1                                      1 ONE                  
     2                                      2 TWO                  
     3                                      3 THREE                
     4                                      4 FOUR                 
     5                                      5 FIVE 

而;

SELECT * FROM 
( SELECT ROWNUM, ID, DESCRIPTION
  FROM T1)
WHERE MOD(ROWNUM,2)=0; 

返回零行???

困惑,预期 ROWNUM=(2,4) 将被返回...

【问题讨论】:

  • 内部选择中的别名 rownum 并引用 where 子句中的别名应该可以工作...
  • SELECT * FROM (SELECT ROWNUM as rn, ID, DESCRIPTION FROM T1) as t WHERE MOD(t.rn, 2)=0;
  • ROWNUMs 是在 WHERE 子句过滤了行之后分配的,因此任何消除 ROWNUM 1 的过滤器都将阻止返回任何行。同样,过滤掉ROWNUM 2 的任何查询都将阻止返回除第 1 行之外的任何行,例如WHERE MOD(ROWNUM, 5) <= 3 将只返回前 3 行,无论表中还有多少行,都等价于 WHERE ROWNUM <= 3 谓词。
  • 这似乎是Selecting the second row of a table using rownum 的变体。无论如何,相同的基本解释。

标签: sql oracle


【解决方案1】:

您有 2ROWNUM 操作。
第一个 ROWNUM 生成数字 1 到 5。
第二个 ROWNUM 不生成任何内容,因为对于该行,ROWNUM 值为 1,但由于 MOD(1,2)=0 为 false,因此没有输出记录并且 ROWNUM 没有递增,一次又一次地失败条件。


这个查询,使用别名,准确地返回你所期望的:

SELECT * FROM 
( SELECT ROWNUM as rn, ID, DESCRIPTION
  FROM T1)
WHERE MOD(rn,2)=0; 

【讨论】:

    【解决方案2】:
    SELECT B.* FROM 
    ( SELECT ROWNUM a, ID, DESCRIPTION
      FROM T1) B
    WHERE MOD(A,2)=0; 
    

    原因:您的方法涉及运行 rownum 两次。你不需要;你也不想。根据操作顺序,where 子句将在外部 select 之前执行;这意味着选择尚未确定每一行的值,并且行数尚不清楚。

    补充:

    我建议将 order by 添加到内联视图中,以便行号处于预期的特定顺序,而不是引擎派生的顺序。

    【讨论】:

    • ??那有什么意思?在创建行时分配行号。引入的第一行被赋值为rownum = 1,它的rownum不为NULL。它只是使 WHERE 子句失败。然后将 rownum = 1 分配给引入的下一行,它再次失败 WHERE,依此类推,直到没有实际选择任何行。根据您的解释, WHERE ROWNUM
    • @mathguy 如果这是真的,那么使用 0 意味着我应该得到所有行吗?也不是这样。所以其他事情正在发生。就像引擎只向前看 1 行,在评估 where 子句时就是这样。 Select rowNum, col1 FROM DUAL CROSS JOIN (Select 'A' col1 from dual UNON SELECT 'B' from dual UNON SELECT 'C' from dual UNON SELECT 'D' from dual) WHERE mod(rownum,2 )<> 0 应该返回 4 行,它只返回第 1 行。 (奇怪的是,我无法在评论中包含正确的 union 拼写)
    • 在哪里使用<>?在mod(rownum, 2)?不 - 在这种情况下,第一行会通过,然后第二行会失败,然后下一行会变成“第二行”并失败,然后是下一行,等等。所以如果你有 mod(rownum, 2) <> 0 你会得到一个行,查询检索到的第一个。
    • 自己尝试一下,让它变得更有趣——试试mod(rownum, 5) <> 0。您将获得前四行,然后每隔一行将变为第五行并失败(因为 rownum=5 每次都被重新分配)。您总共将获得四行,如果一开始会更少,则可能会更少。
    • 有趣。这显然不是我对其运作方式的理解。
    【解决方案3】:

    关于 Oracle 中 ROWNUM 伪列的一些事实:

    • 分配给每一行的ROWNUM 取决于 Oracle 从数据库中检索该行的顺序。
    • 返回行的顺序是不确定的,因此运行一次可能会以一种顺序返回行,如果基表已重新组织,或者 Oracle 使用不同的查询,第二次可能会有不同的顺序计划。
    • ROWNUMs 分配给行的顺序不一定与 order by 子句的顺序相关(order by 子句可能会影响 ROWNUM 顺序,因为它可能会导致使用不同的查询计划,但ROWNUMbers 不太可能与排序顺序匹配)。
    • ROWNUMbers 是在WHERE 子句过滤记录后分配的,因此如果您过滤掉ROWNUM 1,您将永远不会返回任何记录。
    • 过滤返回别名ROWNUM 列的子查询有效,因为整个子查询在外部查询过滤行之前返回到外部查询,但ROWNUMs 仍将具有不确定的顺序。

    要以确定性方式成功返回前 N 或第 N 行查询,您需要以确定性方式分配行号。一种这样的方法是在子查询中使用 `ROW_NUMBER' 分析函数:

    select * from
     (select ROW_NUMBER() over (order by ID) rn
           , ID
           , DESCRIPTION
        from T1)
     where rn <= 4 -- Top N
    

     where rn = 4 -- 1st Nth row
    

    甚至

     WHERE MOD(rn,2)=0 -- every Nth row
    

    在任何一种情况下,ROW_NUMBER 分析函数中的 ORDER BY 子句都需要匹配数据的粒度,否则排序中的关联将再次不确定,很可能匹配当前的 ROWNUM 排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-07
      • 2012-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-12
      • 1970-01-01
      相关资源
      最近更新 更多