Wie schreibt man eine kombinatorische Funktion in Postgres?

Ich habe eine PostgreSQL-Tabelle dieser Form:

base_id int | mods smallint[]
     3      |   {7,15,48}

Ich muss eine Tabelle dieses Formulars ausfüllen:

combo_id int | base_id int | mods smallint[]
     1       |     3       |      
     2       |     3       |      {7}
     3       |     3       |      {7,15}   
     4       |     3       |      {7,48}   
     5       |     3       |      {7,15,48}
     6       |     3       |      {15}
     7       |     3       |      {15,48}
     8       |     3       |      {48}

Ich denke, ich könnte dies mit einer Funktion erreichen, die fast genau dies tut, die die erste Tabelle durchläuft und Kombinationen in die zweite Tabelle schreibt:Generieren Sie alle Kombinationen in SQL

Aber ich bin ein Postgres-Neuling und kann für mein ganzes Leben nicht herausfinden, wie man das mit plpgsql macht. Es muss nicht besonders schnell sein; Es wird nur in regelmäßigen Abständen im Backend ausgeführt. Die erste Tabelle enthält ungefähr 80 Datensätze, und eine grobe Berechnung legt nahe, dass wir für die zweite Tabelle ungefähr 2600 Datensätze erwarten können.

Kann mich wenigstens jemand in die richtige Richtung weisen?

Bearbeiten: Craig: Ich habe PostgreSQL 9.0. Ich konnte erfolgreich UNNEST () verwenden:

FOR messvar IN SELECT * FROM UNNEST(mods) AS mod WHERE mod BETWEEN 0 AND POWER(2, @n) - 1
LOOP
    RAISE NOTICE '%', messvar;
END LOOP;

Aber dann wusste ich nicht, wohin ich als nächstes gehen sollte.

Bearbeiten: Als Referenz habe ich Erwins Lösung verwendet, wobei eine einzelne Zeile hinzugefügt wurde, um jedem Satz ein Null-Ergebnis ('{}') hinzuzufügen, und der Sonderfall, auf den Erwin verweist, wurde entfernt:

CREATE OR REPLACE FUNCTION f_combos(_arr integer[], _a integer[] DEFAULT '{}'::integer[], _z integer[] DEFAULT '{}'::integer[])
RETURNS SETOF integer[] LANGUAGE plpgsql AS
$BODY$
DECLARE
 i int;
 j int;
 _up int;
BEGIN
 IF array_length(_arr,1) > 0 THEN 
    _up := array_upper(_arr, 1);

    IF _a = '{}' AND _z = '{}' THEN RETURN QUERY SELECT '{}'::int[]; END IF;
    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;
          ELSE
             RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
             RETURN QUERY SELECT *
             FROM f_combos(_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$

Dann habe ich diese Funktion verwendet, um meine Tabelle zu füllen:

INSERT INTO e_ecosystem_modified (ide_ecosystem, modifiers) 
(SELECT ide_ecosystem, f_combos(modifiers) AS modifiers FROM e_ecosystem WHERE ecosystemgroup <> 'modifier' ORDER BY ide_ecosystem, modifiers);

Bei 79 Zeilen in meiner Quelltabelle mit maximal 7 Elementen im Modifikator-Array dauerte die Abfrage 250 ms, um 2630 Zeilen in meiner Ausgabetabelle aufzufüllen. Fantastisch.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage