有没有办法强制 Oracle 在 Join Access 之后评估过滤器?
Is there a way to force Oracle to evaluate the Filter after the Join Access?
我想使用强制转换过滤连接的结果。问题是原始字段的一部分无法转换为整数。如果在加入后应用过滤器,则不会有问题。这就是为什么我想知道是否有一种方法(可能是优化器提示或其他东西)在连接操作之后推送过滤器评估。
这是我为示例构建的查询。我希望它能工作,但失败并显示 'ORA-01722 invalid number' :
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 因为它应用于 "literal" 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 Standard Edition 2 Release 18.0.0.0.0 和 Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 上试过这个
非常感谢。
对于where
,使用条件转换。例如:
SELECT *
FROM "scalar" s JOIN
"literal" l
USING ("literal_id")
WHERE (CASE WHEN REGEXP_LIKE(l.literal, '[^[0-9]+$')
THEN TO_NUMBER(l.literal)
END) > 6;
至于你的问题,我不这么认为。 Oracle 有一个非常复杂的优化器,因此它会重新安排操作以优化性能。您可以 使用 CTE 和编译器提示来具体化 CTE,但这似乎有些矫枉过正。
老派技巧是构造查询,使谓词不能被推入连接。如果将联接放入内联视图并添加 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
函数的增强功能 return 如果存在转换错误则为 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;
我想使用强制转换过滤连接的结果。问题是原始字段的一部分无法转换为整数。如果在加入后应用过滤器,则不会有问题。这就是为什么我想知道是否有一种方法(可能是优化器提示或其他东西)在连接操作之后推送过滤器评估。
这是我为示例构建的查询。我希望它能工作,但失败并显示 'ORA-01722 invalid number' :
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 因为它应用于 "literal" 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 Standard Edition 2 Release 18.0.0.0.0 和 Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 上试过这个
非常感谢。
对于where
,使用条件转换。例如:
SELECT *
FROM "scalar" s JOIN
"literal" l
USING ("literal_id")
WHERE (CASE WHEN REGEXP_LIKE(l.literal, '[^[0-9]+$')
THEN TO_NUMBER(l.literal)
END) > 6;
至于你的问题,我不这么认为。 Oracle 有一个非常复杂的优化器,因此它会重新安排操作以优化性能。您可以 使用 CTE 和编译器提示来具体化 CTE,但这似乎有些矫枉过正。
老派技巧是构造查询,使谓词不能被推入连接。如果将联接放入内联视图并添加 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
函数的增强功能 return 如果存在转换错误则为 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;