Использование функции в предложении where, которая включает поиск с нулем - PullRequest
1 голос
/ 27 января 2012

В настоящее время у меня есть подготовленный оператор в Java, который использует следующий оператор SQL в предложении WHERE моего запроса, но я хотел бы переписать это в функцию, чтобы ограничить передаваемые ей параметры пользователя и, возможно, сделать его эффективный.

(
  (USER_PARAM2 IS NULL AND 
    ( COLUMN_NAME = nvl(USER_PARAM1, COLUMN_NAME) OR 
      (nvl(USER_PARAM1, COLUMN_NAME) IS NULL)
    )
  ) 
  OR 
  (USER_PARAM2 IS NOT NULL AND COLUMN_NAME IS NULL)
)

USER_PARAM1 и USER_PARAM2 передаются в подготовленный оператор пользователем. USER_PARAM1 представляет то, что пользователь приложения хочет найти в данном конкретном COLUMN_NAME. Если пользователь не включает этот параметр, он по умолчанию будет иметь значение NULL. USER_PARAM2 был моим способом разрешить пользователю запрашивать поиск только по значению NULL в этом COLUMN_NAME. Кроме того, у меня есть некоторая серверная логика, которая устанавливает для USER_PARAM2 значение «true», если оно передано пользователем, или значение NULL, если оно не было указано пользователем.

Предполагаемое поведение заключается в том, что если было объявлено USER_PARAM2, то возвращаются только значения COLUMN_NAME со значением NULL. Если USER_PARAM2 не был объявлен, а USER_PARAM1 был объявлен, то возвращается только COLUMN_NAME = USER_PARAM1. Если ни один пользовательский параметр не объявлен, возвращаются все строки.

Может ли кто-нибудь помочь мне в этом? Заранее спасибо ...

EDIT: Просто чтобы уточнить, как выглядит мой текущий запрос (без других операторов предложения WHERE ..)

SELECT * 
FROM TABLE_NAME
WHERE (
  (USER_PARAM2 IS NULL AND 
    ( COLUMN_NAME = nvl(USER_PARAM1, COLUMN_NAME) OR 
      (nvl(USER_PARAM1, COLUMN_NAME) IS NULL)
    )
  ) 
  OR 
  (USER_PARAM2 IS NOT NULL AND COLUMN_NAME IS NULL)
)

... и я бы хотел попасть туда ...

SELECT *
FROM TABLE_NAME
WHERE customSearchFunction(USER_PARAM1, USER_PARAM2, COLUMN_NAME)

РЕДАКТИРОВАТЬ # 2: Итак, другой сотрудник помог мне с этим ...

CREATE OR REPLACE function searchNumber (pVal IN NUMBER, onlySearchForNull IN CHAR, column_value IN NUMBER)
  RETURN NUMBER
IS
BEGIN
  IF onlySearchForNull IS NULL THEN 
    IF pVal IS NULL THEN
      RETURN 1;
    ELSE
      IF pVal = column_value THEN
        RETURN 1;
      ELSE
        RETURN 0;
      END IF;  
    END IF;  
  ELSE 
    IF column_value IS NULL THEN
      RETURN 1;
    ELSE
      RETURN 0;
    END IF;  
  END IF;
END;

... это похоже на мои первые испытания ..

SELECT * 
FROM TABLE_NAME
WHERE 1=searchNumber(USER_PARAM1, USER_PARAM2, COLUMN_NAME);

... единственные проблемы, которые у меня были с этим, были бы 1) возможные проблемы производительности по сравнению со сложным оператором SQL, с которого я начал. 2) что мне придется создавать аналогичные функции для каждого типа данных. Тем не менее, последнее будет менее важным для меня.

РЕДАКТИРОВАТЬ # 3 2012.02.01

Итак, мы остановились на решении, которое я выбрал ниже, при использовании подхода, основанного на функциях, где чистота кода / запроса повышает производительность. Мы обнаружили, что подход, основанный на функциях, работает примерно в 6 раз хуже, чем при использовании чистого SQL.

Спасибо всем за отличный вклад!

РЕДАКТИРОВАТЬ # 4 2012.02.14

Итак, оглядываясь назад, я заметил, что применение концепции виртуальной таблицы в решении @ Alan с ясностью решения @ danihp дает очень хорошее общее решение с точки зрения ясности и производительности. Вот что у меня сейчас

WITH   params AS (SELECT user_param1 AS param, user_param2 AS param_nullsOnly FROM DUAL)
SELECT *
FROM   table_name, params p
WHERE  ( nvl(p.param_nullsOnly, p.param) IS NULL                  --1)
         OR p.param_nullsOnly IS NOT NULL AND column_name IS NULL --2)
         OR p.param IS NOT NULL AND column_name = p.param         --3)
       )
-- 1) Test if all rows should be returned
-- 2) Test if only NULL values should be returned
-- 3) Test if param equals the column value

Еще раз спасибо за предложения и комментарии!

Ответы [ 2 ]

2 голосов
/ 28 января 2012

Существует простой способ передать ваши параметры только один раз и обращаться к ним столько раз, сколько необходимо, используя выражения общей таблицы:

WITH params AS (SELECT user_param1 AS up1, user_param2 AS up2 FROM DUAL)
SELECT *
FROM   table_name, params p
WHERE  ((p.up2 IS NULL 
         AND (column_name = NVL(p.up1, column_name) 
              OR (NVL(p.up1, column_name) IS NULL)))
        OR (p.up2 IS NOT NULL AND column_name IS NULL))

По сути, вы создаете виртуальную таблицу, в которой столбцами являются ваши параметры, которая заполняется одной строкой.

Удобно, это также гарантирует, что все ваши параметры собраны в одном и том же месте и могут быть указаны в произвольном порядке (в отличие от порядка, который естественным образом появляется в запросе).

В этом есть пара больших преимуществ по сравнению с функциональным подходом. Во-первых, это не помешает использованию индексов (на что указывает @Bob Jarvis). Во-вторых, это сохраняет логику запроса в запросе, а не скрытую в функциях.

2 голосов
/ 27 января 2012

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

Отправив 2 дополнительных параметра на запрос, вы можете переписать запрос, например:

where
( P_ALL_RESULTS is not null
  OR
  P_ONLY_NULLS is not null AND COLUMN_NAME IS NULL
  OR
  P_USE_P1 is not null AND COLUMN_NAME = USER_PARAM1
)

Отказ от ответственности: ответ перед разъяснением вопроса ОП

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...