PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как да избегнем множество функции eval със синтаксиса (func()).* в SQL заявка?

Можете да го обвиете в подзаявка, но това не е гарантирано безопасно без OFFSET 0 хаквам. В 9.3 използвайте LATERAL . Проблемът е причинен от ефективно макроразширяващия анализатор * в списък с колони.

Заобиколно решение

Къде:

SELECT (my_func(x)).* FROM some_table;

ще оцени my_func n пъти за n колони с резултати от функцията, тази формулировка:

SELECT (mf).* FROM (
    SELECT my_func(x) AS mf FROM some_table
) sub;

обикновено няма да има и има тенденция да не добавя допълнително сканиране по време на изпълнение. За да гарантирате, че множествената оценка няма да бъде извършена, можете да използвате OFFSET 0 хакване или злоупотреба. Неуспехът на PostgreSQL да оптимизира извън границите на CTE:

SELECT (mf).* FROM (
    SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;

или:

WITH tmp(mf) AS (
    SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;

В PostgreSQL 9.3 можете да използвате LATERAL за да постигнете по-разумно поведение:

SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;

LEFT JOIN LATERAL ... ON true запазва всички редове като оригиналната заявка, дори ако извикването на функцията не връща ред.

Демо

Създайте функция, която не е вградена като демонстрация:

CREATE OR REPLACE FUNCTION my_func(integer)
RETURNS TABLE(a integer, b integer, c integer) AS $$
BEGIN
    RAISE NOTICE 'my_func(%)',$1;
    RETURN QUERY SELECT $1, $1, $1;
END;
$$ LANGUAGE plpgsql;

и таблица с фиктивни данни:

CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;

след това опитайте горните версии. Ще видите, че първото повдига три известия на извикване; последните вдигат само един.

Защо?

Добър въпрос. Ужасно е.

Изглежда така:

(func(x)).*

се разширява като:

(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l

при синтактичен анализ, според поглед към debug_print_parse , debug_print_rewritten и debug_print_plan . (отрязаното) дърво за синтактичен анализ изглежда така:

   :targetList (
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
                 ...
            }
         :fieldnum 1 
         :resulttype 23 
         :resulttypmod -1 
         :resultcollid 0
         }
      :resno 1 
      :resname i 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
                 ...
            }
         :fieldnum 2 
         :resulttype 20 
         :resulttypmod -1 
         :resultcollid 0
         }
      :resno 2 
      :resname j 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
             ...
            }
         :fieldnum 3 
         :...
         }
      :resno 3 
      :resname k 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
             ...
            }
         :fieldnum 4 
          ...
         }
      :resno 4 
      :resname l 
       ...
      }
   )

По принцип използваме тъп хак за анализатор, за да разширим заместващи знаци чрез клониране на възли.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да разберете дали upsert е актуализация с PostgreSQL 9.5+ UPSERT?

  2. Таблица като аргумент на функция на PostgreSQL

  3. Създаване на PostgreSQL последователност към поле (което не е ID на записа)

  4. Как да прочетем съдържанието на .sql файл в R скрипт, за да изпълним заявка?

  5. Вместо LIKE и ~, защо само SIMILAR TO работи, когато правите съвпадение на регулярни изрази с алтернативи