EXISTS не работает с подзапросом в предложении WITH - PullRequest
6 голосов
/ 29 мая 2019

Я столкнулся с запросом, в котором предложение 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 )

После игры с некоторыми тестовыми данными, я думаю (частично?) ответственная строка за сбой - это подзапрос AND ReferenceDate >= ( SELECT StartDate FROM StartDateTable WHERE ItemId = :itemId ) во втором WITH.

Я обнаружил, что любое из следующих изменений приводит к тому, что EXISTS снова работает, как и ожидалось:

  • JOIN ResultTable с SecondDateFilter (on ReferenceDate)
  • Put ( 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 )
;

1 Ответ

3 голосов
/ 29 мая 2019

Согласно комментариям Джонатана в Твиттере , предлагаемое решение - использовать подсказку unnest во внешнем подзапросе "существует", поскольку проблема связана с ошибкой (потенциально ошибка 28319114).

[...]
SELECT ReferenceDate, Value
FROM ResultTable
WHERE ItemId = 1
AND EXISTS ( SELECT /*+ UNNEST */ * FROM SecondDateFilter WHERE SecondDateFilter.ReferenceDate = ResultTable.ReferenceDate )
...