Я пытаюсь сделать часть этого запроса условной, но мне это очень сложно. Ниже я напечатал псевдокод, отражающий мой запрос.
Основной запрос
SELECT A.T_NBR
,A.I_NBR
,A.DATE
,A.MX_CD
,A.MY_CD
,A.S_CD
,B.O_NBR
FROM A,B,C,D,E,F,H,I,TMP_J
WHERE A.MY_CD IN ('FOO','BAR')
AND A.T_NBR NOT IN (
SELECT A.T_NBR
FROM A,K
WHERE A.MY_CD = 'BAR'
AND P_DATE < ADD_MONTHS(SYSDATE, -36)
AND A.S_CD NOT IN ('AAA','BBB')
AND K.K_CD NOT IN ('DDD','EEE')
--JOIN
AND A.I_NBR = K.I_NBR(+)
)
AND U_CD = 'FFFF'
--DATE LESS THAN 3 YEARS
AND TO_DATE(H.DATE,'YYYY-MM-DD') < ADD_MONTHS(SYSDATE, -36)
--NUM GREATER THAN 23
AND TO_CHAR(SYSDATE,'SYYYY')-TO_CHAR(I.NUM,'SYYYY') > 23
AND A.S_CD NOT IN ('AAA','BBB')
AND B.DATE = (
SELECT MAX(DATE)
FROM B
)
AND B.O_NBR < 200
AND F.MG_CD <> '000'
AND A.T_NBR NOT IN (12345,54321,98765)
AND ((E.T IS NULL) OR (RTRIM(E.T) LIKE '%###%'))
AND TMP_J.I_NBR IS NULL
--JOINS
AND A.T_BR = B.T_NBR(+)
AND ((B.I_NBR = F.I_NBR) AND (B.DATE = F.DATE))
AND A.S_NBR = D.S_NBR(+)
AND A.D_NBR = C.D_NBR(+)
AND ((D.S_NBR = G.S_NBR(+)) AND (UPPER(G.A_CD(+)) = 'XXX'))
AND G.A_NBR = E.A_NBR(+)
AND H.I_NBR = A.I_NBR
AND A.S_NBR = I.S_NBR(+)
AND A.T_NBR = TMP_J.T_NBR(+);
По сути, это то, что я пытаюсь сделать условным (написанным на простом английском языке с помощью псевдокода):
ЕСЛИ
A.MY_CD = 'BAR'
ЗАТЕМ
A.T_NBR NOT IN (
SELECT A.T_NBR
FROM A,K
WHERE A.MY_CD = 'BAR'
AND P_DATE < ADD_MONTHS(SYSDATE, -36)
AND A.S_CD NOT IN ('AAA','BBB')
AND K.K_CD NOT IN ('DDD','EEE')
--JOIN
AND A.I_NBR = K.I_NBR(+)
)
ИНАЧЕ, ЕСЛИ
A.MY_CD = 'FOO'
ЗАТЕМ
WHERE U_CD = 'FFFF'
--DATE LESS THAN 3 YEARS
AND TO_DATE(H.DATE,'YYYY-MM-DD') < ADD_MONTHS(SYSDATE, -36)
--NUM GREATER THAN 23
AND TO_CHAR(SYSDATE,'SYYYY')-TO_CHAR(I.NUM,'SYYYY') > 23
И ТОГДА Я ПРОСТО ХОЧУ, ЧТОБЫ ОСТАЛЬНЫЕ ПРЕДЛОЖЕНИЯ WHERE ДЕЙСТВУЮТ ВНЕ УСЛОВИЯ.
Помимо того, что я не знаю, какой метод будет работать лучше всего (CASE/WHEN, DECODE, IF/THEN), часть, с которой я действительно борюсь, - это попытка реализовать оператор SELECT внутри этих условных предложений. Я попытался реализовать CASE WHEN в частях запроса SELECT и WHERE, одновременно пытаясь поместить оператор SELECT в часть WHEN оператора CASE. Я также попробовал DECODE таким же образом. Кроме того, я попытался реализовать IF/THEN, но, похоже, не смог связать его с моим основным запросом.
Я, конечно, не профессионал в PL/SQL, так что, возможно, это просто моя ошибка в знаниях. Как лучше всего сделать часть этого запроса условной?
Большое спасибо за ваши ответы и помощь!
... но я дам вам несколько трюков, которые (насколько мне известно) безвредны - например, «отключение» предиката или термина путем установки параметров в значения, которые механизм БД безопасно исключает как константное исключение на этапе планирования. , например, в запросах диапазона дат. Другой подход - посмотреть, можете ли вы фильтровать, используя полусоединение или анти-соединение вместо термина равенства - и так далее...
«Хороший» способ — начать использовать явные предложения JOIN, избегая использования NOT IN вместе с подзапросами, вы можете предпочесть использование NOT EXISTS.
@BarbarosÖzhan Какие проблемы ты видишь с NOT IN? Даже если он очень избирательен в наборе данных для запроса?
В основном я сталкивался с плохой производительностью запросов (медленное выполнение) для запросов с NOT IN, за которыми следуют подзапросы. @Дай


Кажется, вы просто хотите объединить условия с логикой И/ИЛИ; без внесения каких-либо других изменений в ваш подход:
...
FROM A,B,C,D,E,F,H,I,TMP_J
WHERE
(
(
A.MY_CD = 'BAR'
AND A.T_NBR NOT IN (
SELECT A.T_NBR
FROM A,K
WHERE A.MY_CD = 'BAR'
AND P_DATE < ADD_MONTHS(SYSDATE, -36)
AND A.S_CD NOT IN ('AAA','BBB')
AND K.K_CD NOT IN ('DDD','EEE')
--JOIN
AND A.I_NBR = K.I_NBR(+)
)
)
OR
(
A.MY_CD = 'FOO'
AND U_CD = 'FFFF'
--DATE LESS THAN 3 YEARS
AND TO_DATE(H.DATE,'YYYY-MM-DD') < ADD_MONTHS(SYSDATE, -36)
--NUM GREATER THAN 23
AND TO_CHAR(SYSDATE,'SYYYY')-TO_CHAR(I.NUM,'SYYYY') > 23
)
)
AND A.S_CD NOT IN ('AAA','BBB')
AND B.DATE = (
SELECT MAX(DATE)
FROM B
)
AND B.O_NBR < 200
AND F.MG_CD <> '000'
AND A.T_NBR NOT IN (12345,54321,98765)
AND ((E.T IS NULL) OR (RTRIM(E.T) LIKE '%###%'))
AND TMP_J.I_NBR IS NULL
--JOINS
...
Логично, что это то, о чем вы просите. Но вам нужно будет, помимо прочего, посмотреть на план выполнения и производительность. Есть и другие вопросы, о которых стоит подумать, включая переключение на использование явных соединений, not in и not exists (как упоминалось в комментариях), отказ от хранения дат в виде строк, возможно, использование (более современных) соединений вместо подзапросов, возможно, использование extract() для получения номер года вместо to_char() и неявное преобразование в число (если это предложение вообще имеет смысл) и т. д.
Спасибо, сэр! Это то, что я искал, я просто слишком много думал об этом.
Вот как я бы об этом подумал:
IF A = X
THEN
IF B = Y
CASE 1
ELSE
CASE 2
ELSE
IF C = Z
CASE 3
Можно переписать как
IF (A = X) AND (B = Y)
CASE 1
IF (A = X) AND (B != Y)
CASE 2
IF (A != X) AND (C = Z)
CASE 3
Это можно перевести в оператор SQL WHERE следующим образом:
WHERE
-- Case 1
((A = X) AND (B = Y)) -- you can put more clauses using AND inside ()
OR
-- Case 2
((A = X) AND (B != Y)) -- you can put more clauses using AND inside ()
OR
-- Case 3
((A != X) AND (C = Z)) -- you can put more clauses using AND inside ()
«Лучший» способ — это построение запроса в виде строки с использованием динамического SQL, который затем выполняется. Это связано с тем, что сам SQL не поддерживает динамические предикаты (и мне неизвестны какие-либо расширения поставщиков или попытки их создания). Я советую вам избегать таких хаков, как
WHERE ( @paramA IS NULL OR colA= @paramA ), потому что они всегда портят кэш плана запроса или выбор индекса.