EXISTS 不适用于 WITH 子句中的子查询
Posted
技术标签:
【中文标题】EXISTS 不适用于 WITH 子句中的子查询【英文标题】:EXISTS not working with subquery in WITH clause 【发布时间】:2019-05-29 06:50:01 【问题描述】:我遇到了 EXISTS 子句无法正常工作的查询。即使对于不存在匹配记录的项目,查询也会返回结果,似乎完全忽略了 EXISTS。它曾经工作正常,我认为从 Oracle 12.1 升级到 12.2 后问题就开始了。
以下是完整的查询(仅更改了表和列名以使其更具可读性,但我保留了所有逻辑以防与此相关):
WITH FirstDateFilter AS (
SELECT ReferenceDate,
Type,
LAG(Type, 1, 0) OVER (ORDER BY ReferenceDate) AS PreviousType
FROM ReferenceDateTable
WHERE ItemId = :itemId
AND ReferenceDate <= :endDate
AND Type IN (:type1, :type2)
), SecondDateFilter AS (
SELECT ReferenceDate
FROM FirstDateFilter
WHERE ReferenceDate >= :startDate
AND ReferenceDate >= ( SELECT StartDate FROM StartDateTable WHERE ItemId = :itemId )
AND Type = :type1
AND PreviousType = :type1
)
SELECT ReferenceDate, Value
FROM ResultTable
WHERE ItemId = :itemId
AND EXISTS ( SELECT * FROM SecondDateFilter WHERE SecondDateFilter.ReferenceDate = ResultTable.ReferenceDate )
在使用了一些测试数据之后,我认为(部分?)导致失败的原因是第二个 WITH 中的子查询 AND ReferenceDate >= ( SELECT StartDate FROM StartDateTable WHERE ItemId = :itemId )
。
我发现以下任何编辑都会导致 EXISTS 再次按预期工作:
使用 SecondDateFilter 加入 ResultTable(在 ReferenceDate 上) 将( SELECT ReferenceDate FROM SecondDateFilter WHERE SecondDateFilter.ReferenceDate = ResultTable.ReferenceDate )
放入SELECT ... FROM ResultTable
注释掉 StartDateTable 子查询(不再过滤该表,否则它会再次起作用)
将 StartDateTable 子查询移至第一个 WITH
最后一个解决方案实际上解决了我这个查询的问题(技术上不一样,但底层业务逻辑检查结果总是一样的),但我想知道 EXISTS 子句是否存在一般问题(可能仅在 Oracle 12.2 中?)我应该知道。我有更多使用它的查询。
下面是一个重复错误的测试脚本。下面的查询按预期返回 2 行,但删除注释行会得到 5 行。
CREATE TABLE ReferenceDateTable
(
ItemId number,
ReferenceDate date,
Type varchar2(1)
);
INSERT INTO ReferenceDateTable (ItemId, ReferenceDate, Type) VALUES (1, to_date('19000201', 'YYYYMMDD'), '1');
INSERT INTO ReferenceDateTable (ItemId, ReferenceDate, Type) VALUES (1, to_date('19000202', 'YYYYMMDD'), '1');
INSERT INTO ReferenceDateTable (ItemId, ReferenceDate, Type) VALUES (1, to_date('19000203', 'YYYYMMDD'), '2');
INSERT INTO ReferenceDateTable (ItemId, ReferenceDate, Type) VALUES (1, to_date('19000204', 'YYYYMMDD'), '1');
INSERT INTO ReferenceDateTable (ItemId, ReferenceDate, Type) VALUES (1, to_date('19000205', 'YYYYMMDD'), '1');
CREATE TABLE ResultTable
(
ItemId number,
ReferenceDate date,
Value number
);
INSERT INTO ResultTable (ItemId, ReferenceDate, Value) VALUES (1, to_date('19000201', 'YYYYMMDD'), 1);
INSERT INTO ResultTable (ItemId, ReferenceDate, Value) VALUES (1, to_date('19000202', 'YYYYMMDD'), 2);
INSERT INTO ResultTable (ItemId, ReferenceDate, Value) VALUES (1, to_date('19000203', 'YYYYMMDD'), 3);
INSERT INTO ResultTable (ItemId, ReferenceDate, Value) VALUES (1, to_date('19000204', 'YYYYMMDD'), 4);
INSERT INTO ResultTable (ItemId, ReferenceDate, Value) VALUES (1, to_date('19000205', 'YYYYMMDD'), 5);
CREATE TABLE StartDateTable
(
ItemId number,
StartDate date
);
INSERT INTO StartDateTable (ItemId, StartDate) VALUES (1, to_date('19000101', 'YYYYMMDD'));
WITH FirstDateFilter AS (
SELECT ReferenceDate,
Type,
LAG(Type, 1, 0) OVER (ORDER BY ReferenceDate) AS PreviousType
FROM ReferenceDateTable
WHERE ItemId = 1
AND ReferenceDate <= to_date('19000205', 'YYYYMMDD')
AND Type IN ('1', '2')
), SecondDateFilter AS (
SELECT ReferenceDate
FROM FirstDateFilter
WHERE ReferenceDate >= to_date('19000201', 'YYYYMMDD')
--AND ReferenceDate >= ( SELECT StartDate FROM StartDateTable WHERE ItemId = 1 )
AND Type = '1'
AND PreviousType = '1'
)
SELECT ReferenceDate, Value
FROM ResultTable
WHERE ItemId = 1
AND EXISTS ( SELECT * FROM SecondDateFilter WHERE SecondDateFilter.ReferenceDate = ResultTable.ReferenceDate )
;
【问题讨论】:
请您在查询中添加一些示例数据(例如,使用 referencedatetable 作为 (select 1 id, 'a' col2, .... from dual union all select 2 id, 'b' .. ..) 重现了这个问题,然后我们也可以试一试。 @Boneist 添加了重现问题的示例脚本 @Boneist 我尝试使用 select from dual 输入单个查询,但我无法以这种方式重现问题 这对我来说绝对是一个错误。 Here's a db<>fiddle demonstrating the issue 但如果你把它切换回 11g,它就可以工作(两个版本都是 Oracle XE)。我还在 [LiveSql](livesql.oracle.com/apex/livesql/s/igfmwvw0id0y4xyrp8mqysx66) 中运行了语句,即 19c,这也显示了问题。我还包括了重写的两个查询(在 dbfiddle 和 LiveSQL 中)以避免使用 with 子句,问题也出现在那里。 Jonathan Lewis 看了看(感谢 Jonathan!)并回复 over on Twitter 说它在 18.3 中仍然存在问题,可能与错误 28319114:由于复杂的视图合并导致 12.2 中的错误结果有关。他建议的解决方法是在最终存在子查询中添加不嵌套提示。 【参考方案1】:根据Jonathan's comments over on Twitter,建议的解决方法是在外部exists 子查询中使用unnest
提示,因为问题是由错误引起的(可能是错误28319114)。
[...]
SELECT ReferenceDate, Value
FROM ResultTable
WHERE ItemId = 1
AND EXISTS ( SELECT /*+ UNNEST */ * FROM SecondDateFilter WHERE SecondDateFilter.ReferenceDate = ResultTable.ReferenceDate )
【讨论】:
以上是关于EXISTS 不适用于 WITH 子句中的子查询的主要内容,如果未能解决你的问题,请参考以下文章
带有“Exists”子句的子查询,其中需要从两个表中收集信息