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

postgresで組み合わせ関数を書く方法は?

    私がそれを眠った後、私は完全に新しく、より単純で、より速い考えを思いつきました:

    CREATE OR REPLACE FUNCTION f_combos(_arr anyarray)
      RETURNS TABLE (combo anyarray) LANGUAGE plpgsql AS
    $BODY$
    BEGIN
        IF array_upper(_arr, 1) IS NULL THEN
            combo := _arr; RETURN NEXT; RETURN;
        END IF;
    
        CASE array_upper(_arr, 1)
    --  WHEN 0 THEN -- does not exist
        WHEN 1 THEN
            RETURN QUERY VALUES ('{}'), (_arr);
        WHEN 2 THEN
            RETURN QUERY VALUES ('{}'), (_arr[1:1]), (_arr), (_arr[2:2]);
        ELSE
            RETURN QUERY
            WITH x AS (
                SELECT f.combo FROM f_combos(_arr[1:array_upper(_arr, 1)-1]) f
                )
            SELECT x.combo FROM x
            UNION ALL
            SELECT x.combo || _arr[array_upper(_arr, 1)] FROM x;
        END CASE;
    END
    $BODY$;
    

    電話:

    SELECT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
    

    512行、合計実行時間:2.899ミリ秒

    説明

    • NULLで特殊なケースを処理する と空の配列。
    • 2つのプリミティブ配列の組み合わせを作成します。
    • それより長い配列は次のように分類されます:
      • 長さn-1の同じ配列の組み合わせ
      • さらに要素nと組み合わせたものすべて..再帰的に

    一度手に入れたら、本当に簡単です。

    • 添え字1で始まる1次元整数配列で機能します (以下を参照)。
    • 古いソリューションの2〜3倍の速度で、拡張性が向上します。
    • anyで機能します 再び要素タイプ(ポリモーフィックタイプを使用)。
    • 質問に表示されているように(そして@Craigがコメントで私に指摘したように)、結果に空の配列を含めます。
    • より短く、よりエレガント。

    これは、配列の添え字を前提としています。 1から開始 (デフォルト)。値がわからない場合は、次のような関数を呼び出して正規化します。

    SELECT * FROM  f_combos(_arr[array_lower(_arr, 1):array_upper(_arr, 1)]);
    

    配列の添え字を正規化するためのより洗練された方法があるかどうかはわかりません。私はそれについて質問を投稿しました:
    1次元配列の配列添え字を正規化して1

    古いソリューション(遅い)

    CREATE OR REPLACE FUNCTION f_combos2(_arr int[], _a int[] = '{}', _z int[] = '{}')
     RETURNS SETOF int[] LANGUAGE plpgsql AS
    $BODY$
    DECLARE
       i int;
       j int;
       _up int;
    BEGIN
       IF array_length(_arr,1) > 0 THEN 
          _up := array_upper(_arr, 1);
    
          FOR i IN array_lower(_arr, 1) .. _up LOOP
             FOR j IN i .. _up  LOOP
                CASE j-i
                WHEN 0,1 THEN
                   RETURN NEXT _a || _arr[i:j] || _z;
                WHEN 2 THEN
                   RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
                   RETURN NEXT _a || _arr[i:j] || _z;
                ELSE
                   RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
                   RETURN QUERY SELECT *
                   FROM f_combos2(_arr[i+1:j-1], _a || _arr[i], _arr[j] || _z);
                END CASE;
             END LOOP;
          END LOOP;
       ELSE
          RETURN NEXT _arr;
       END IF;
    END;
    $BODY$;
    

    電話:

    SELECT * FROM f_combos2('{7,15,48}'::int[]) ORDER BY 1;
    

    1次元整数配列で機能します。これはさらに最適化できますが、この質問の範囲では確かに必要ありません。
    ORDER BY 質問に表示された順序を課します。

    NULLとして、NULLまたは空の配列を指定します コメントに記載されています。

    PostgreSQL 9.1でテストされていますが、中途半端な最新バージョンで動作するはずです。 array_lower() およびarray_upper() 少なくともPostgreSQL7.4以降は存在しています。バージョン8.4では、パラメーターのデフォルトのみが新しくなっています。簡単に交換できます。

    パフォーマンスはまともです。

    SELECT DISTINCT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
    

    511行、合計実行時間:7.729ミリ秒

    説明

    このシンプルなフォームに基づいて構築されています 隣接する要素のすべての組み合わせのみを作成します:

    CREATE FUNCTION f_combos(_arr int[])
      RETURNS SETOF int[] LANGUAGE plpgsql AS
    $BODY$
    DECLARE
       i  int;
       j  int;
      _up int;
    BEGIN
       _up := array_upper(_arr, 1);
    
       FOR i in array_lower(_arr, 1) .. _up LOOP
          FOR j in i .. _up LOOP
             RETURN NEXT _arr[i:j];
          END LOOP;
       END LOOP;
    END;
    $BODY$;
    

    ただし、これは3つ以上の要素を持つサブ配列では失敗します。だから:

    • 3つの要素を持つサブ配列の場合、外側の2つの要素だけを持つ1つの配列が追加されます。これは、パフォーマンスを向上させるこの特殊なケースのショートカットであり、厳密には必要ありません

    • 3つ以上の要素を持つサブ配列の場合、外側の2つの要素を使用します。 内部要素のすべての組み合わせを入力します 同じ関数によって再帰的に構築されます 。



    1. SQLServer2016の一時テーブルクエリプランの動作

    2. ORACLEのselectステートメントのフィールドのデータ型を取得します

    3. MySQLでsprintfに相当するものはありますか?

    4. Oracleで日付をフォーマットするときの曜日と月の名前の大文字化