【问题标题】:cx_Oracle ignores order by clausecx_Oracle 忽略 order by 子句
【发布时间】:2016-08-31 22:23:36
【问题描述】:

我在我的项目中创建了复杂的查询构建器,并且在测试期间偶然发现了奇怪的问题:具有相同计划的相同查询在不同的客户端上产生不同的结果:cx_Oracle 忽略 order by 子句,而 Oracle SQLDeveloper Studio 可以正确处理查询在这两种情况下,都按两个计划中的存在顺序排列。

有问题的查询是:

select *
from
(
    select 
        a.*,
        ROWNUM tmp__rnum
    from
    (
        select base.* 
        from
        (
            select id
            from
            (
                (
                    select 
                    profile_id as id,
                    surname as sort__col 
                    from names
                )
                /* here usually are several other subqueries chained by unions */
            )
            group by id
            order by min(sort__col) asc
        ) tmp
        left join (profiles) base
        on tmp.id = base.id
        where exists 
        (
            select t.object_id 
            from object_rights t 
            where
                    t.object_id = base.id 
                and t.subject_id = :a__subject_id 
                and t.rights in ('r','w') 
        )
    ) a 
    where ROWNUM < :rows_to 
) 
where tmp__rnum >= :rows_from

并从 cx_Oracle 计划以防我错过任何事情:

{'operation': 'SELECT STATEMENT', 'position': 9225, 'cardinality': 2164, 'time': 1, 'cost': 9225, 'depth': 0, 'bytes': 84396, 'optimizer': 'ALL_ROWS', 'id': 0, 'cpu_cost': 1983805801},

    {'operation': 'VIEW', 'position': 1, 'filter_predicates': '"TMP__RNUM">=TO_NUMBER(:ROWS_FROM)', 'parent_id': 0, 'object_instance': 1, 'cardinality': 2164SEL$1', 'projection': '"from$_subquery$_001"."ID"[NUMBER,22], "from$_subquery$_001"."CREATION_TIME"[TIMESTAMP,11], "TMP__RNUM"[NUMBER,22]', 'time': 1, 'cost': 9225, 'depth': 1, 'bytes': 84396, 'id': 1, 'cpu_cost': 1983805801},

        {'operation': 'COUNT', 'position': 1, 'filter_predicates': 'ROWNUM<TO_NUMBER(:ROWS_TO)', 'parent_id': 1, 'projection': '"BASE"."ID"[NUMBER,22], "BASE"."CREATION_TIME"[TIMESTAMP,11], ROWNUM[8]', 'options': 'STOPKEY', 'depth': 2, 'id': 2, 

            {'operation': 'HASH JOIN', 'position': 1, 'parent_id': 2, 'access_predicates': '"TMP"."ID"="BASE"."ID"', 'cardinality': 2164, 'projection': '(#keys=1) "BASE"."ID"[NUMBER,22], "BASE"."CREATION_TIME"[TIMESTAMP,11]', 'time': 1, 'cost': 9225, 'depth': 3, 'bytes': 86560, 'id': 3, 'cpu_cost': 1983805801},

                {'operation': 'JOIN FILTER', 'position': 1, 'parent_id': 3, 'object_owner': 'SYS', 'cardinality': 2219, 'projection': '"BASE"."ID"[NUMBER,22], "BASE"."CREATION_TIME"[TIMESTAMP,11]', 'object_name': ':BF0000', 'time': 1, 'cost': 662, 'options': 'CREATE', 'depth': 4, 'bytes': 59913, 'id': 4, 'cpu_cost': 223290732},

                    {'operation': 'HASH JOIN', 'position': 1, 'parent_id': 4, 'access_predicates': '"T"."OBJECT_ID"="BASE"."ID"', 'cardinality': 2219, 'projection': '(#keys=1) "BASE"."ID"[NUMBER,22], "BASE"."CREATION_TIME"[TIMESTAMP,11]', 'time': 1, 'cost': 662, 'options': 'RIGHT SEMI', 'depth': 5, 'bytes': 59913, 'id': 5, 'cpu_cost': 223290732},

                        {'operation': 'TABLE ACCESS', 'position': 1, 'filter_predicates': '"T"."SUBJECT_ID"=TO_NUMBER(:A__SUBJECT_ID) AND ("T"."RIGHTS"=\'r\' OR "T"."RIGHTS"=\'w\')', 'parent_id': 5, 'object_type': 'TABLE', 'object_instance': 8, 'cardinality': 2219, 'projection': '"T"."OBJECT_ID"[NUMBER,22]', 'object_name': 'OBJECT_RIGHTS', 'time': 1, 'cost': 5, 'options': 'FULL', 'depth': 6, 'bytes': 24409, 'optimizer': 'ANALYZED', 'id': 6, 'cpu_cost': 1823386},

                        {'operation': 'TABLE ACCESS', 'position': 2, 'parent_id': 5, 'object_type': 'TABLE', 'object_instance': 6, 'cardinality': 753862, 'projection': '"BASE"."ID"[NUMBER,22], "BASE"."CREATION_TIME"[TIMESTAMP,11]', 'object_name': 'PROFILES', 'time': 1, 'cost': 654, 'options': 'FULL', 'depth': 6, 'bytes': 12061792, 'optimizer': 'ANALYZED', 'id': 7, 'cpu_cost': 145148296},

                {'operation': 'VIEW', 'position': 2, 'parent_id': 3, 'object_instance': 3, 'cardinality': 735296, 'projection': '"TMP"."ID"[NUMBER,22]', 'time': 1, 'cost': 8559, 'depth': 4, 'bytes': 9558848, 'id': 8, 'cpu_cost': 1686052619},

                    {'operation': 'SORT', 'position': 1, 'parent_id': 8, 'cardinality': 735296, 'projection': '(#keys=1) MIN("SURNAME")[50], "PROFILE_ID"[NUMBER,22]', 'time': 1, 'cost': 8559, 'options': 'ORDER BY', 'temp_space': 18244000, 'depth': 5, 'bytes': 10294144, 'id': 9, 'cpu_cost': 1686052619},

                        {'operation': 'HASH', 'position': 1, 'parent_id': 9, 'cardinality': 735296, 'projection': '(#keys=1; rowset=200) "PROFILE_ID"[NUMBER,22], MIN("SURNAME")[50]', 'time': 1, 'cost': 8559, 'options': 'GROUP BY', 'temp_space': 18244000, 'depth': 6, 'bytes': 10294144, 'id': 10, 'cpu_cost': 1686052619},

                            {'operation': 'JOIN FILTER', 'position': 1, 'parent_id': 10, 'object_owner': 'SYS', 'cardinality': 756586, 'projection': '(rowset=200) "PROFILE_ID"[NUMBER,22], "SURNAME"[VARCHAR2,50]', 'object_name': ':BF0000', 'time': 1, 'cost': 1202, 'options': 'USE', 'depth': 7, 'bytes': 10592204, 'id': 11, 'cpu_cost': 190231639},

                                {'operation': 'TABLE ACCESS', 'position': 1, 'filter_predicates': 'SYS_OP_BLOOM_FILTER(:BF0000,"PROFILE_ID")', 'parent_id': 11, 'object_type': 'TABLE', 'object_instance': 5, 'cardinality': 756586, 'projection': '(rowset=200) "PROFILE_ID"[NUMBER,22], "SURNAME"[VARCHAR2,50]', 'object_name': 'NAMES', 'time': 1, 'cost': 1202, 'options': 'FULL', 'depth': 8, 'bytes': 10592204, 'optimizer': 'ANALYZED', 'id': 12, 'cpu_cost': 190231639}

cx_Oracle 输出(似乎按 id 排序):

ID,    Created,    rownum
(1829, 2016-08-24, 1)
(2438, 2016-08-24, 2)

SQLDeveloper 输出(按姓氏排序,如预期):

ID,      Created,    rownum
(518926, 2016-08-28, 1)
(565556, 2016-08-29, 2)

【问题讨论】:

    标签: python sql oracle cx-oracle


    【解决方案1】:

    我没有看到会影响查询结果排序的ORDER BY 子句。在 SQL 中,保证结果集排序的唯一方法是为最外层的SELECT 设置一个ORDER BY 子句。

    在几乎所有情况下,子查询中的 ORDER BY 不一定受到尊重(当查询的下一级有 rownum 比较时,Oracle 会例外——即使现在已经过时了支持FETCH FIRST &lt;n&gt; ROWS)。

    因此,没有理由期望 innermost 子查询中的 ORDER BY 会产生任何影响,尤其是随后发生的 JOIN。

    建议:

    • 将ORDER BY 移动到最外层的查询。
    • 如果您使用的是 Oracle 12c+,请使用 FETCH FIRST 语法。
    • 将ORDER BY移到JOIN之后。
    • 使用ROW_NUMBER() 而不是rownum。

    【讨论】:

    • 这仍然无法解释这里的真正问题——不同客户端的不同行为(它们都连接到同一个数据库)。
    • @elmon 。 . .以不确定顺序返回结果的查询可能会在不同时间或从不同环境中运行。
    • 感谢您的回答。我很好奇,环境如何取决于查询结果?是否存在影响查询处理的隐藏设置/变量?结果并不是纯粹随机的——对于以相同方式形成的不同查询,这种行为已经观察了很长时间,而且从我的角度来看,这完全取决于环境。优化器缓存他随机选择的行为? (这只是可能有意义的疯狂猜测)。
    • 当您省略order by 子句时,会影响返回结果顺序的因素太多。答案总是,如果您需要按特定顺序返回它们,请添加order by 子句;否则,接受他们可以以任何顺序返回,不确定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2011-09-07
    相关资源
    最近更新 更多