Аналог Postgres для перекрестного применения в SQL Server

мне нужно перенести SQL-запросы, написанные для MS SQL Server 2005, в Postgres 9.1.
Какой лучший способ заменить CROSS APPLY в этом запросе?

SELECT *
FROM V_CitizenVersions         
CROSS APPLY     
       dbo.GetCitizenRecModified(Citizen, LastName, FirstName, MiddleName,
BirthYear, BirthMonth, BirthDay, ..... ) -- lots of params

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

4 ответов


В Postgres 9.3 или позже используйте LATERAL вступайте:

SELECT v.col_a, v.col_b, f.*  -- no parentheses here, f is a table alias
FROM   v_citizenversions v
LEFT   JOIN LATERAL f_citizen_rec_modified(v.col1, v.col2) f ON true
WHERE  f.col_c = _col_c;

почему LEFT JOIN LATERAL ... ON true?


на старые версии, есть очень простой способ сделать то, что я думаю вы пытаетесь с помощью функции set-returning (RETURNS TABLE или RETURNS SETOF record или RETURNS record):

SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM   v_citizenversions v

функция вычисляет значения один раз для каждой строки внешнего запроса. Если функция возвращает несколько строк, результирующие строки умножаются соответственно. Все!--11-->скобки синтаксически обязательны разложить тип строки. Функция таблицы может выглядеть примерно так:

CREATE OR REPLACE FUNCTION f_citizen_rec_modified(_col1 int, _col2 text)
  RETURNS TABLE(col_c integer, col_d text) AS
$func$
SELECT s.col_c, s.col_d
FROM   some_tbl s
WHERE  s.col_a = 
AND    s.col_b = 
$func$ LANGUAGE sql;

вам нужно обернуть это в подзапрос или CTE, если вы хотите применить WHERE предложение, потому что столбцы не видны на одном и том же уровень. (И это лучше для производительности в любом случае, потому что вы предотвращаете повторную оценку для каждого выходного столбца функции):

SELECT col_a, col_b, (f_row).*
FROM (
   SELECT col_a, col_b, f_citizen_rec_modified(col1, col2) AS f_row
   FROM   v_citizenversions v
   ) x
WHERE (f_row).col_c = _col_c;

есть несколько других способов сделать это или нечто подобное. Все зависит от того, чего именно вы хотите.


некромантии:
Новое в PostgreSQL 9.3:

боковое ключевое слово

левый | правый / внутренний JOIN боковая

INNER JOIN LATERAL это то же самое, что CROSS APPLY
и LEFT JOIN LATERAL это то же самое, что OUTER APPLY

пример использования:

SELECT * FROM T_Contacts 

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989


LEFT JOIN LATERAL 
(
    SELECT 
         --MAP_CTCOU_UID    
         MAP_CTCOU_CT_UID   
        ,MAP_CTCOU_COU_UID  
        ,MAP_CTCOU_DateFrom 
        ,MAP_CTCOU_DateTo   
   FROM T_MAP_Contacts_Ref_OrganisationalUnit 
   WHERE MAP_CTCOU_SoftDeleteStatus = 1 
   AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID 

    /*  
    AND 
    ( 
        (__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) 
        AND 
        (__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) 
    ) 
    */
   ORDER BY MAP_CTCOU_DateFrom 
   LIMIT 1 
) AS FirstOE 

мне нравится ответить Эрвин Брандштеттер, однако, я обнаружил проблемы с производительностью : при запуске

SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM   v_citizenversions v

функция f_citizen_rec_modified будет выполняться 1 раз для каждого возвращаемого столбца (умножается на каждую строку в v_citizenversions). Я не нашел документации для этого эффекта, но смог вывести его путем отладки. Теперь возникает вопрос, как мы можем получить этот эффект (до 9.3, где доступны боковые соединения) без этой стороны ограбления производительности эффект?

Update: кажется, я нашел ответ. Переписать запрос следующим образом:

select x.col1, x.col2, x.col3, (x.func).* 
FROM (select SELECT v.col1, v.col2, v.col3, f_citizen_rec_modified(col1, col2) func
FROM   v_citizenversions v) x

ключевым отличием является получение результатов необработанной функции сначала (внутренний подзапрос), а затем обертывание этого в другой select, который выводит эти результаты в столбцы. Это было протестировано на PG 9.2


эта ссылка показывает, как это сделать в Postgres 9.0+:

PostgreSQL: параметризация рекурсивного CTE

Это дальше по странице в разделе под названием "эмуляция перекрестного применения с функциями возврата набора". Пожалуйста, не забудьте отметить список ограничений после примера.