【问题标题】:Wrong result from statement with rownum带有 rownum 的语句的错误结果
【发布时间】:2018-04-25 17:09:31
【问题描述】:

我在使用 Hibernate 生成的 SQL 语句时遇到了一些问题。我想在给定的时间范围内或特定的日子让所有用户的生日。虽然特定日期的语句工作正常,但另一个只返回前 n 个用户,其中 n 是正确用户的数量。

考虑到这些数据:

ID     name      birthdate
1      firstUser 12.07.1990
2      user1     25.04.2007
3      user2     15.05.1992
4      user3     01.04.1988

首先生成特定日期的语句:

select
    * 
from
    ( select
        distinct *
    from
        USERS user0_ 
    where
        calculateNextBirthday(user0_.birthDate) = TO_DATE('25.04.2018', 'dd.MM.yyyy')
    order by
        user0_.id asc ) 
where
    rownum <= ?; // This will be the row count of this statement with the same where clause, i.e. 1 if there is only 1 matching user

正如我所说,此语句返回正确的用户(ID 为 2 的 user1)。

相同的语句,中间有:

select
    * 
from
    ( select
        distinct *
    from
        USERS user0_ 
    where
        calculateNextBirthday(user0_.birthDate) between TO_DATE('01.04.2018', 'dd.MM.yyyy') and TO_DATE('30.04.2018', 'dd.MM.yyyy')
    order by
        user0_.id asc ) 
where
    rownum <= ?; // This will be the row count of this statement with the same where clause, i.e. 1 if there is only 1 matching user

这个语句有一个奇怪的行为。如果我执行内部选择语句,结果将包含正确的用户 (user1)。但执行整个语句将返回第一个用户(firstUser,ID 为1)。像这样更改日期:

select
    * 
from
    ( select
        distinct *
    from
        USERS user0_ 
    where
        calculateNextBirthday(user0_.birthDate) between TO_DATE('01.04.2018', 'dd.MM.yyyy') and TO_DATE('30.06.2018', 'dd.MM.yyyy')
    order by
        user0_.id asc ) 
where
    rownum <= ?; // This will be the row count of this statement with the same where clause, i.e. 1 if there is only 1 matching user

将返回前两个用户(firstUseruser1),因为有两个匹配的用户(user1user2

如果我从 where 外部语句中删除 rownum,它只会返回正确的用户(下面的示例返回 user1)。

select
    * 
from
    ( select
        distinct *
    from
        USERS user0_ 
    where
        calculateNextBirthday(user0_.birthDate) between TO_DATE('01.04.2018', 'dd.MM.yyyy') and TO_DATE('30.04.2018', 'dd.MM.yyyy')
    order by
        user0_.id asc ) 

所以我不知道 Oracle 从哪里获得第一个用户条目。

我在这里做错了什么还是 Oracle Oracle Database 10g Express Edition Release 10.2.0.1.0 中存在错误?

【问题讨论】:

  • rownum 不明确 - 它显然来自内部选择
  • @Randy 如何将 rownum 应用于内部选择?特别是因为它适用于 equal。
  • 添加了更多细节。我希望它有助于理解我的问题。并且 rownumber 具有正确的编号。我不想要更多行,我想要正确的行。
  • 您所做的似乎在我的 11.2.0.4 环境和 in an SQL Fiddle 中正常工作。还尝试了略有不同的数据which also works。您使用的是 Oracle 的哪个版本和补丁级别?您能否创建一个可重现的示例,最好是在另一个 SQL Fiddle 中?
  • 好吧,我的错误比我想象的要老。现在是 10.2。我还尝试使用 SQL Fiddle 创建一个示例。

标签: sql oracle oracle11g


【解决方案1】:

似乎对我有用(11g)。不过,我不使用您的 calculateNextBirthday 函数:

SQL> drop table t_users
Table dropped.
SQL> create table t_users
(
id number,
name varchar2(100),
birthdate date
)
Table created.
SQL> insert into t_users (id,name,birthdate) values (1,'firstUser',to_date('12.07.1990','DD.MM.YYYY'))
1 row created.
SQL> insert into t_users (id,name,birthdate) values (2,'user1',to_date('25.04.2007','DD.MM.YYYY'))
1 row created.
SQL> insert into t_users (id,name,birthdate) values (3,'user2',to_date('15.05.1992','DD.MM.YYYY'))
1 row created.
SQL> insert into t_users (id,name,birthdate) values (4,'user3',to_date('01.04.1988','DD.MM.YYYY'))
1 row created.
SQL> commit
Commit complete.
SQL> select
    * 
from
    ( select
        distinct *
    from
        t_users user0_ 
    where
        to_date( to_char(birthdate, 'DD.MM') || '.' || to_char(sysdate, 'YYYY'), 'DD.MM.YYYY' ) between TO_DATE('01.04.2018', 'dd.MM.yyyy') and TO_DATE('30.04.2018', 'dd.MM.yyyy')
    order by
        user0_.id asc ) 
where
    rownum <= 2

        ID
----------
NAME                                                                            
--------------------------------------------------------------------------------
BIRTHDATE
---------
         2
user1                                                                           
25-APR-07

         4
user3                                                                           
01-APR-88


2 rows selected.

【讨论】:

    【解决方案2】:

    在这种情况下,您不能依赖 rownum,因为 order by 在内部选择中。 Rownum 是在 order by 之前确定的,因此覆盖了 order by。一般来说,依赖 rownum 并不是一个好习惯。

    需要注意的是,我了解 Oracle SQL 并且对 Hibernate 并不熟悉,我的建议如下

    我会考虑使用您需要的信息创建一个视图,并查看 Hibernate 是否可以更好地处理,否则我会尝试使用仅使用内部选择的语句覆盖 Hibernate 生成的 SQL 语句,因为没有 Oracle 原因用外部语句包装该选择。

    【讨论】:

    • 你的第一句话不正确。这是正确的模式 - 内部查询中的顺序,在外部查询中使用 rownumThis is explained in the documentation.
    • 不幸的是,Oracle 文档并不总是反映现实世界的使用情况。我在理论上同意这应该像写的那样工作,但作者说它在实践中并没有,而且我的经验表明依赖 rownum 会导致这样的问题。
    猜你喜欢
    • 2021-03-22
    • 1970-01-01
    • 1970-01-01
    • 2015-12-28
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-27
    相关资源
    最近更新 更多