Лучший способ создать условный SQL-запрос? СЛУЧАЙ, ДЕКОДЕ или ЕСЛИ/ТО?

Я пытаюсь сделать часть этого запроса условной, но мне это очень сложно. Ниже я напечатал псевдокод, отражающий мой запрос.

Основной запрос

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, так что, возможно, это просто моя ошибка в знаниях. Как лучше всего сделать часть этого запроса условной?

Большое спасибо за ваши ответы и помощь!

«Лучший» способ — это построение запроса в виде строки с использованием динамического SQL, который затем выполняется. Это связано с тем, что сам SQL не поддерживает динамические предикаты (и мне неизвестны какие-либо расширения поставщиков или попытки их создания). Я советую вам избегать таких хаков, как WHERE ( @paramA IS NULL OR colA= @paramA ), потому что они всегда портят кэш плана запроса или выбор индекса.

Dai 27.03.2024 17:03

... но я дам вам несколько трюков, которые (насколько мне известно) безвредны - например, «отключение» предиката или термина путем установки параметров в значения, которые механизм БД безопасно исключает как константное исключение на этапе планирования. , например, в запросах диапазона дат. Другой подход - посмотреть, можете ли вы фильтровать, используя полусоединение или анти-соединение вместо термина равенства - и так далее...

Dai 27.03.2024 17:08

«Хороший» способ — начать использовать явные предложения JOIN, избегая использования NOT IN вместе с подзапросами, вы можете предпочесть использование NOT EXISTS.

Barbaros Özhan 27.03.2024 17:09

@BarbarosÖzhan Какие проблемы ты видишь с NOT IN? Даже если он очень избирательен в наборе данных для запроса?

Dai 27.03.2024 17:19

В основном я сталкивался с плохой производительностью запросов (медленное выполнение) для запросов с NOT IN, за которыми следуют подзапросы. @Дай

Barbaros Özhan 27.03.2024 17:55
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
5
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Кажется, вы просто хотите объединить условия с логикой И/ИЛИ; без внесения каких-либо других изменений в ваш подход:

...
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() и неявное преобразование в число (если это предложение вообще имеет смысл) и т. д.

Спасибо, сэр! Это то, что я искал, я просто слишком много думал об этом.

Crimp 27.03.2024 18:18

Вот как я бы об этом подумал:

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 () 

  

Другие вопросы по теме