使用fetch first rows only子句执行两次命令

时间:2017-11-30 12:26:46

标签: sql oracle

我正在尝试检索这样的查询的第一行:

    select tbl.a
      from tbl
     where tbl.code = 5
  order by util_test.getPrio(tbl.id)
     fetch first 1 rows only;

util_test.getPrio()返回一个我用于排序结果的整数。 因为我只对最好的结果感兴趣,所以我只获取第一行。

查询(没有'fetch first')只返回一行,但是,util_test.getPrio()包含一个dbms_output,如果我添加'fetch first 1 rows',我会看到相同的输出2次。如果我不这样做,我只会看到输出一次,就像预期的那样。

我对解决方案并不感兴趣(从性能的角度来看,我知道这很糟糕), 我只是想知道为什么?

2 个答案:

答案 0 :(得分:4)

因为order by发生在fetch first ... rows only之前。否则,您将获得任何行,而不是util_test.getPrio(tbl.id)返回的最低值的行。

换句话说,您可能有两行符合where条件。这些都是排序的,然后是第一个。

修改

在Oracle中,fetch first ... rows only在内部被重写为窗口函数。沿着这个:

SELECT *
  FROM (SELECT *
             , ROW_NUMBER() OVER(ORDER BY <HERE>) rn
          FROM tbl
         WHERE tbl.code = 5
       )
 WHERE rn <= 1

如果您愿意,可以尝试“旧的”Oracle appraoch,看看问题是否仍然存在。这可以使用rownum伪列而不是窗口函数ROW_NUMBER()

SELECT *
  FROM (SELECT *
             , rownum rn
          FROM tbl
         WHERE tbl.code = 5
         ORDER BY <HERE>
       )
 WHERE rn <= 1

不幸的是,上次检查时,“旧”方法仍比fetch first更快:/

答案 1 :(得分:1)

我感谢order by中的一个函数每次被引用时都会被调用 - 虽然这可能不是确定性函数。

我希望函数可以调用n * log(n)次,其中n是查询其余部分返回的行数。

使用子查询很容易解决这个问题:

select a
from (select tbl.a, util_test.getPrio(tbl.id) as pri
      from tbl
      where tbl.code = 5
     ) t
order by pri
fetch first 1 rows only;

请注意,这将在子查询生成的每行中调用一次函数。