web-dev-qa-db-ja.com

SQL ServerのCROSS APPLYに類似したPostgres

MS SQL Server 2005用に作成されたSQLクエリをPostgres 9.1に移行する必要があります。
このクエリで_CROSS APPLY_を置き換える最良の方法は何ですか?

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

GetCitizenRecModified() functionは、テーブル値関数です。この関数のコードは非常に巨大なので配置できません。計算が難しくなり、それを放棄することができません。

19
user1178399

Postgres9.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


古いバージョンの場合、非常に簡単な方法がありますI thinkセットを返す関数で試みています( RETURNS TABLEまたはRETURNS SETOF record OR RETURNS record ):

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

この関数は、外部クエリのすべての行に対して一度値を計算します。関数が複数の行を返す場合、結果の行はそれに応じて乗算されます。行タイプを分解するには、すべての括弧が構文的に必要です。テーブル関数は次のようになります。

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 = $1
AND    s.col_b = $2
$func$ LANGUAGE sql;

列が同じレベルで表示されないため、WHERE句を適用する場合は、これをサブクエリまたはCTEでラップする必要があります。 (そして、関数のすべての出力列の繰り返し評価を防ぐため、とにかくパフォーマンスが向上します):

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;

これを行うには、他にもいくつかの方法があります。それはあなたが正確に何を望むかに依存します。

15

ネクロマンシング:
PostgreSQL 9.3の新機能:

LATERALキーワード

左|右|内部結合[〜#〜]ラテラル[〜#〜]

INNER JOIN LATERALCROSS APPLYと同じです
およびLEFT JOIN LATERALOUTER 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 
12
Stefan Steiger

Erwin Brandstetterの答えが好きですが、パフォーマンスの問題を発見しました。

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

F_citizen_rec_modified関数は、それが返すすべての列に対して1回実行されます(v_citizenversionsのすべての行で乗算されます)。この効果に関するドキュメントは見つかりませんでしたが、デバッグすることで推測できました。問題は、パフォーマンスを奪う副作用なしに、どのようにしてこの効果を得ることができるかです(9.3より前では、横方向の結合が利用可能です)。

更新:答えを見つけたようです。クエリを次のように書き換えます。

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でテストされました

2
Joe Love

このリンクは、Postgres 9.0以降でそれを行う方法を示すように見えます。

PostgreSQL:再帰CTEのパラメーター化

これは、「set-returning関数を使用したCROSS APPLYのエミュレート」というタイトルのセクションのさらに下にあります。例の後の制限のリストに注意してください。

1
Matthew Wood