sql - 有没有办法强制 Oracle 在加入访问后评估过滤器?

标签 sql oracle oracle11g oracle18c

我想使用强制转换来过滤连接的结果。问题是原始字段的一部分无法转换为整数。如果在连接之后应用过滤器,则不会有问题。这就是为什么我想知道是否有一种方法(可能是优化器提示或其他东西)在连接操作之后推送过滤器评估。

这是我为示例构建的查询。我希望它能工作,但因“ORA-01722 无效数字”而失败:

WITH "literal" AS (
    SELECT 1 AS "literal_id", 'abc' AS "literal"
    FROM "DUAL"
    UNION
    SELECT 2 AS "literal_id", '7' AS "literal"
    FROM "DUAL"
),
     "scalar" AS (
         SELECT 3 AS "scalar_id", 2 AS "literal_id"
         FROM "DUAL"
         CONNECT BY ROWNUM <= 10000
     )
SELECT *
FROM "scalar"
         JOIN "literal" USING ("literal_id")
WHERE TO_NUMBER("literal") > 6;

抛出 ORA-01722 是因为它应用于“文字”CTE,因此崩溃,因为“abc”显然不是数字。 我们可以在执行计划中看到这一点:

Query execution plan

为了减少问题原因的可能性,我执行了该查询:

CREATE TABLE "literal" AS (
    SELECT 1 AS "literal_id", 'abc' AS "literal"
    FROM "DUAL"
    UNION
    SELECT 2 AS "literal_id", '7' AS "literal"
    FROM "DUAL"
);
CREATE TABLE "scalar" AS (
    SELECT 3 AS "scalar_id", 2 AS "literal_id"
    FROM "DUAL"
    CONNECT BY ROWNUM <= 10000
);
CREATE TABLE "joined" AS (
    SELECT *
    FROM "scalar"
             JOIN "literal" USING ("literal_id")
);
SELECT *
FROM "joined"
WHERE TO_NUMBER("literal") > 6;

效果非常好。

那么,有没有办法重写这个查询(尽管我仍然需要将其作为单个查询),这样它就不会尝试转换“abc”?

作为引用,我在 Oracle Database 18c 标准版 2 版本 18.0.0.0.0 以及 Oracle Database 11g 企业版版本 11.2.0.1.0 上尝试过此操作

非常感谢。

最佳答案

老派的技巧是构造查询,使得谓词不能被插入连接中。如果您将联接放入内联 View 并添加 rownum,这将阻止优化器在联接完成之前评估谓词

WITH "literal" AS (
    SELECT 1 AS "literal_id", 'abc' AS "literal"
    FROM "DUAL"
    UNION
    SELECT 2 AS "literal_id", '7' AS "literal"
    FROM "DUAL"
),
     "scalar" AS (
         SELECT 3 AS "scalar_id", 2 AS "literal_id"
         FROM "DUAL"
         CONNECT BY ROWNUM <= 10000
     )
select *
  from (
SELECT "scalar_id",  "literal_id", "literal", rownum
FROM "scalar"
         JOIN "literal" USING ("literal_id")
)
WHERE TO_NUMBER("literal") > 6;

如果您使用的是 12.2 或更高版本,则可以利用 to_number 函数的增强功能,在出现转换错误时返回 NULL。

WITH "literal" AS (
    SELECT 1 AS "literal_id", 'abc' AS "literal"
    FROM "DUAL"
    UNION
    SELECT 2 AS "literal_id", '7' AS "literal"
    FROM "DUAL"
),
     "scalar" AS (
         SELECT 3 AS "scalar_id", 2 AS "literal_id"
         FROM "DUAL"
         CONNECT BY ROWNUM <= 10000
     )
SELECT "scalar_id",  "literal_id", "literal"
FROM "scalar"
         JOIN "literal" USING ("literal_id")
WHERE to_number("literal" default null on conversion error) > 6;

关于sql - 有没有办法强制 Oracle 在加入访问后评估过滤器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56777528/

相关文章:

带域用户的 sqlpackage.exe

php - 知道哪一列有结果

MySQL 触发器自动更新(或插入)列字段

oracle - 执行 BULK COLLECT 时出现 ORA-00932

oracle - Postgresql 中 Oracle 的 UTL_RAW.BIT_OR 的等价物

sql - 查询性能非常差

oracle - 在同一个嵌套表上批量收集两次

MySQL:使用 Union 将多个 Select 查询拆分到各自的表中

sql - 使用 Oracle SQL 连接同一列中的 2 个值

java - 解析 PL/SQL 代码以检查语法和语义错误