sql >> データベース >  >> RDS >> PostgreSQL

ループイン機能が期待どおりに機能しない

    たくさんあります 私は違ったやり方で、そして大きな効果をもたらすでしょう。

    テーブル定義

    テーブルの定義と命名規則から始めます。これらはほとんど単なる意見です:

    CREATE TEMP TABLE conta (conta_id bigint primary key, ...);
    
    CREATE TEMP TABLE departamento (
       dept_id   serial PRIMARY KEY
     , master_id int REFERENCES departamento (dept_id)
     , conta_id  bigint NOT NULL REFERENCES conta (conta_id)
     , nome      text NOT NULL
    );
    

    主なポイント

    • bigserial> 部門のために?この惑星にはそれほど多くはありません。プレーンなシリアル 十分なはずです。

    • 文字の変化を使用することはほとんどありません。 長さ制限あり。他のいくつかのRDBMSとは異なり、制限を使用してもパフォーマンスはまったく向上しません。 CHECKを追加します 本当に最大長を強制する必要がある場合は制約。 テキストを使用しています 、主にそして、トラブルを回避します。

    • 外部キー列が参照される列と名前を共有する命名規則を提案するので、 master_id master_fkの代わりに 、など。 USINGの使用も許可されます 参加します。

    • そして、私はめったに わかりにくい列名idを使用します 。 dept_idの使用 代わりにここに。

    PL/pgSQL関数

    これは主に次のように簡略化できます。

    CREATE OR REPLACE FUNCTION f_retornar_plpgsql(lista_ini_depts VARIADIC int[])
      RETURNS int[] AS
    $func$
    DECLARE
       _row departamento;                     -- %ROWTYPE is just noise
    BEGIN
    
    IF NOT EXISTS (                           -- simpler in 9.1+, see below
        SELECT FROM pg_catalog.pg_class
        WHERE  relnamespace = pg_my_temp_schema()
        AND    relname      = 'tbl_temp_dptos') THEN
    
       CREATE TEMP TABLE tbl_temp_dptos (dept_id bigint NOT NULL)
       ON COMMIT DELETE ROWS;
    END IF;
    
    FOR i IN array_lower(lista_ini_depts, 1)  -- simpler in 9.1+, see below
          .. array_upper(lista_ini_depts, 1) LOOP
       SELECT *  INTO _row                    -- since rowtype is defined, * is best
       FROM   departamento
       WHERE  dept_id = lista_ini_depts[i];
    
       CONTINUE WHEN NOT FOUND;
    
       INSERT INTO tbl_temp_dptos VALUES (_row.dept_id);
    
       LOOP
          SELECT *  INTO _row
          FROM   departamento
          WHERE  dept_id = _row.master_id;
    
          EXIT WHEN NOT FOUND;
    
          INSERT INTO tbl_temp_dptos
          SELECT _row.dept_id
          WHERE  NOT EXISTS (
             SELECT FROM tbl_temp_dptos
             WHERE dept_id =_row.dept_id);
       END LOOP;
    END LOOP;
    
    RETURN ARRAY(SELECT dept_id FROM tbl_temp_dptos);
    
    END
    $func$  LANGUAGE plpgsql;
    

    電話:

    SELECT f_retornar_plpgsql(2, 5);
    

    または:

    SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
    
    • ALIAS FOR $ 1 は古い構文であり、推奨されていません 。代わりに関数パラメータを使用してください。

    • VARIADIC パラメータを使用すると、呼び出しがより便利になります。関連:

    • EXECUTEは必要ありません 動的要素のないクエリの場合。ここで得られるものはありません。

    • テーブルを作成するために例外処理は必要ありません。マニュアルの引用こちら :

    • Postgres9.1以降にはCREATEがあります存在しない場合のTEMPテーブル 。 9.0の回避策を使用して、条件付きで一時テーブルを作成します。

    • Postgres 9.1は、 FOREACHも提供します。 配列をループする

    とは言うものの、ここに厄介な問題があります。これのほとんどは必要ありません。

    rCTEを使用したSQL関数

    Postgres 9.0でも、再帰CTE これを非常に簡単にします :

    CREATE OR REPLACE FUNCTION f_retornar_sql(lista_ini_depts VARIADIC int[])
      RETURNS int[] AS
    $func$
    WITH RECURSIVE cte AS (
       SELECT dept_id, master_id
       FROM   unnest($1) AS t(dept_id)
       JOIN   departamento USING (dept_id)
    
       UNION ALL
       SELECT d.dept_id, d.master_id
       FROM   cte
       JOIN   departamento d ON d.dept_id = cte.master_id
       )
    SELECT ARRAY(SELECT DISTINCT dept_id FROM cte)    -- distinct values
    $func$  LANGUAGE sql;
    

    同じ電話。

    説明付きの密接に関連した回答:

    SQLフィドルが両方を示しています。



    1. PHP/MySQLタイムスタンプをより魅力的に見せます

    2. MavenGAEプラグイン+GoogleCloudSQLのエラー

    3. mysqlは選択時に無効な日付を許可します

    4. mysqliクエリのエコー