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.