Возвращать строки по умолчанию из функции, когда первый SELECT не возвращает строки - PullRequest
0 голосов
/ 16 мая 2018

У меня есть эта функция http://rextester.com/VIHMIG61446

CREATE OR REPLACE FUNCTION myTestProcedure(namevalue character varying)
 RETURNS TABLE(id integer, name character varying, isdefault boolean)
 LANGUAGE plpgsql
AS $function$
  BEGIN
    IF EXISTS(SELECT
        Domain.id,
        Domain.name,
        Domain.isdefault
      FROM Domain
      where lower(Domain.name) like namevalue)
    THEN
      RETURN QUERY SELECT
        Domain.id,
        Domain.name,
        Domain.isdefault
      FROM Domain
      where lower(Domain.name) like namevalue;
    ELSE
     RETURN QUERY SELECT
        Domain.id,
        Domain.name,
        Domain.isdefault
      FROM Domain
      where Domain.isdefault = true;
    END IF;   
  END
$function$;

и я ищу способ не повторять весь запрос для if, поэтому я решил использовать with as для сохранения результата, но он не работает для меня http://rextester.com/MVMVA73088

Как мне использовать with as?

CREATE OR REPLACE FUNCTION myTestProcedure(namevalue character varying)
 RETURNS TABLE(id integer, name character varying, isdefault boolean)
 LANGUAGE plpgsql
AS $function$
  BEGIN
    with temporal_result as (
      SELECT
        Domain.id,
        Domain.name,
        Domain.isdefault
      FROM Domain
      where lower(Domain.name) like namevalue
    )
    IF EXISTS(temporal_result)
    THEN
      RETURN QUERY SELECT * from temporal_result;
    ELSE
     RETURN QUERY SELECT
        Domain.id,
        Domain.name,
        Domain.isdefault
      FROM Domain
      where Domain.isdefault = true;
    END IF;   
  END
$function$;

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Предлагаю проверить специальную переменную plpgsql FOUND вместо:

CREATE OR REPLACE FUNCTION my_test_func(namevalue varchar)
  RETURNS TABLE(id integer, name varchar, isdefault boolean) AS
$func$
BEGIN
   RETURN QUERY 
   SELECT d.id, d.name, d.isdefault
   FROM   domain d
   WHERE  lower(d.name) LIKE namevalue;

   IF NOT FOUND THEN
      RETURN QUERY 
      SELECT d.id, d.name, d.isdefault
      FROM   domain d
      WHERE  d.isdefault;
   END IF;   
END
$func$  LANGUAGE plpgsql STABLE;

Очистить и быстро .Первый запрос прост и максимально быстр.Второй запрос никогда не выполняется, когда первый возвращает какие-либо строки.Связано (см. Главу «Другие случаи»):

Я бывероятно, используйте d.name ILIKE namevalue и поддержите его индексом триграммы.См .:

И частичный индекс для строк по умолчанию во втором запросе может заплатить,тоже.Если вы можете получить из него только индексные сканирования, включите (несколько) столбцов, которые нужно извлечь как индексные столбцы:

CREATE INDEX domain_defaults_idx ON domain (id, name, isdefault) WHERE isdefault;

Да, мы должны включить isdefault, хотя и логически избыточно.Postgres в настоящее время (стр. 10) не достаточно умен, чтобы получить значение из условия WHERE.

Если вы не можете получить сканирование только по индексу, индекс с константным выражением дешевле:

CREATE INDEX domain_defaults_idx ON domain ((TRUE)) WHERE isdefault;

Похожие:

0 голосов
/ 16 мая 2018

Логики if-else можно полностью избежать.Эквивалентный результат может быть записан как один запрос.Функция BOOL_AND является агрегатной функцией, которая возвращает false, если любое из значений равно false, в противном случае она возвращает true.

Следующий запрос будет работать правильно, даже если несколько строк соответствуют условию lower(name) like '<namevalue>' или если у вас есть несколько значений по умолчанию.

SELECT subquery.id, subquery.name, subquery.isdefault
    FROM (SELECT d.id,
       d.name,
       d.isdefault,
       BOOL_AND(d.isdefault) OVER () default_and
    FROM domain d
    WHERE lower(d.name) like 'robert' or isdefault) subquery
WHERE isdefault = default_and

Относительно того, почему вы получаете ошибку сIF EXISTS(temporal_result), это недействительно sql.Запрещается делать такое ветвление внутри оператора SQL.Вместо этого вы можете сохранить результат первого запроса во временную таблицу и выполнить ветвление if-else со ссылкой на временную таблицу.Ниже приведена правильная версия хранимой процедуры:

CREATE OR REPLACE FUNCTION mytestprocedure(namevalue character varying)
 RETURNS TABLE(id integer, name character varying, isdefault boolean)
 LANGUAGE plpgsql
AS $function$
  BEGIN
    CREATE TEMPORARY TABLE temporal_result as 
      SELECT
        d.id,
        d.name,
        d.isdefault
      FROM domain d
      where lower(d.name) like namevalue
    ;
    IF EXISTS(SELECT TRUE FROM temporal_result) THEN
      RETURN QUERY SELECT * from temporal_result;
    ELSE
      RETURN QUERY SELECT
        d.id,
        d.name,
        d.isdefault
      FROM domain d
      where d.isdefault = true;
      END IF;  
  DROP TABLE temporal_result;
  RETURN;
  END;
$function$;

Обратите внимание, что необходимо удалить таблицу в конце процедуры.

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

...