Gibt es eine Möglichkeit, eine benannte Konstante in einer PostgreSQL-Abfrage zu definieren? Zum Beispiel:
MY_ID = 5;
SELECT * FROM users WHERE id = MY_ID;
Diese Frage wurde zuvor gestellt ( Wie verwendet man Skriptvariablen in PostgreSQL? ). Es gibt jedoch einen Trick, den ich manchmal für Abfragen verwende:
with const as (
select 1 as val
)
select . . .
from const cross join
<more tables>
Das heißt, ich definiere einen CTE namens const mit den dort definierten Konstanten. Ich kann dies dann auf jeder Ebene beliebig oft in meine Abfrage einfügen. Ich habe dies besonders nützlich gefunden, wenn ich mit Datumsangaben arbeite und Datumskonstanten in vielen Unterabfragen behandeln muss.
PostgreSQL hat keine eingebaute Möglichkeit, (globale) Variablen wie MySQL oder Oracle zu definieren. (Es gibt eine begrenzte Problemumgehung mit "benutzerdefinierten Optionen" ). Je nachdem, was Sie genau wollen, gibt es andere Möglichkeiten:
Sie können Werte oben in einer Abfrage in einem CTE wie @Gordon angeben, das bereits bereitgestellt wurde.
Sie könnten dazu eine einfache IMMUTABLE
-Funktion erstellen:
CREATE FUNCTION public.f_myid()
RETURNS int IMMUTABLE LANGUAGE SQL AS
'SELECT 5';
Es muss in einem Schema leben, das sichtbar für den aktuellen Benutzer ist, d. H. Sich im jeweiligen search_path
befindet. Wie das Schema public
standardmäßig. Wenn die Sicherheit ein Problem ist, stellen Sie sicher, dass es das erste Schema in search_path
ist oder das Schema in Ihrem Aufruf qualifiziert:
SELECT public.f_myid();
Sichtbar für alle Benutzer in der Datenbank (die auf das Schema public
zugreifen dürfen).
CREATE TEMP TABLE val (val_id int PRIMARY KEY, val text);
INSERT INTO val(val_id, val) VALUES
( 1, 'foo')
, ( 2, 'bar')
, (317, 'baz');
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM val WHERE val_id = $1';
SELECT f_val(2); -- returns 'baz'
Da plpgsql das Vorhandensein einer Tabelle beim Erstellen überprüft, müssen Sie eine (temporäre) Tabelle val
erstellen, bevor Sie die Funktion erstellen können - auch wenn eine temporäre Tabelle am Ende der Sitzung gelöscht wird, während die Funktion bestehen bleibt. Die Funktion löst eine Ausnahme aus, wenn die zugrunde liegende Tabelle zum Zeitpunkt des Aufrufs nicht gefunden wird.
Das aktuelle Schema für temporäre Objekte liegt standardmäßig vor dem Rest Ihres search_path
- sofern nicht ausdrücklich anders angegeben. Das temporäre Schema aus search_path
kann nicht ausschließen, aber Sie können andere Schemas zuerst einfügen.
Schlechte Kreaturen der Nacht (mit den erforderlichen Privilegien) können mit dem search_path
basteln und ein anderes Objekt desselben Namens voranstellen:
CREATE TABLE myschema.val (val_id int PRIMARY KEY, val text);
INSERT INTO val(val_id, val) VALUES (2, 'wrong');
SET search_path = myschema, pg_temp;
SELECT f_val(2); -- returns 'wrong'
Dies ist keine große Bedrohung, da nur privilegierte Benutzer die globalen Einstellungen ändern können. Andere Benutzer können dies nur für ihre eigene Sitzung tun. Um dies zu verhindern, überhaupt, legen Sie den search_path
für Ihre Funktion fest und qualifizieren Sie ihn im Aufruf:
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM val WHERE val_id = $1'
SET search_path = pg_temp;
Oder benutze stattdessen:
... SET search_path = pg_temp, param;
Dies würde Ihnen (oder jedem, der über die erforderlichen Berechtigungen verfügt) ermöglichen, globale (permanente) Standardwerte in einer Tabelle bereitzustellen param.val
...
Beachten Sie das entsprechende Kapitel des Handbuchs zum Erstellen von Funktionen mit SECURITY DEFINER .
Diese äußerst sichere Funktion kann jedoch nicht "eingebettet" werden und ist möglicherweise langsamer als eine einfachere Alternative mit einem fest verdrahteten Schema:
CREATE FUNCTION f_val(_id int)
RETURNS text STABLE LANGUAGE SQL AS
'SELECT val FROM pg_temp.val WHERE val_id = $1';
Verwandte Antworten mit mehr Optionen:
Zusätzlich zu den sinnvollen Optionen, die Gordon und Erwin bereits erwähnt haben (temporäre Tabellen, ständig wiederkehrende Funktionen, CTEs usw.), können Sie (ab) den PostgreSQL-GUC-Mechanismus verwenden, um Variablen auf globaler, Sitzungs- und Transaktionsebene zu erstellen.
Siehe diesen vorherigen Beitrag der den Ansatz im Detail zeigt.
Ich empfehle dies nicht für den allgemeinen Gebrauch, aber es kann in engen Fällen nützlich sein, wie z. B. in der verlinkten Frage, in der das Plakat eine Möglichkeit suchte, den Benutzernamen auf Anwendungsebene für Auslöser und Funktionen bereitzustellen.
Ich habe diese Lösung gefunden:
with vars as (
SELECT * FROM (values(5)) as t(MY_ID)
)
SELECT * FROM users WHERE id = (SELECT MY_ID FROM vars)
Ich habe eine Mischung der verfügbaren Ansätze für am besten gefunden:
CREATE TABLE vars (
id INT NOT NULL PRIMARY KEY DEFAULT 1,
zipcode INT NOT NULL DEFAULT 90210,
-- etc..
CHECK (id = 1)
);
CREATE FUNCTION generate_var_getter()
RETURNS VOID AS $$
DECLARE
var_name TEXT;
var_value TEXT;
new_rows TEXT[];
new_sql TEXT;
BEGIN
FOR var_name IN (
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_schema = 'public'
AND columns.table_name = 'vars'
ORDER BY columns.ordinal_position ASC
) LOOP
EXECUTE
FORMAT('SELECT %I FROM vars LIMIT 1', var_name)
INTO var_value;
new_rows := ARRAY_APPEND(
new_rows,
FORMAT('(''%s'', %s)', var_name, var_value)
);
END LOOP;
new_sql := FORMAT($sql$
CREATE OR REPLACE FUNCTION var_get(key_in TEXT)
RETURNS TEXT AS $config$
DECLARE
result NUMERIC;
BEGIN
result := (
SELECT value FROM (VALUES %s)
AS vars_tmp (key, value)
WHERE key = key_in
);
RETURN result;
END;
$config$ LANGUAGE plpgsql IMMUTABLE;
$sql$, ARRAY_TO_STRING(new_rows, ','));
EXECUTE new_sql;
RETURN;
END;
$$ LANGUAGE plpgsql;
generate_var_getter()
aufgerufen wird und die unveränderliche var_get()
-Funktion neu erstellt wird.CREATE FUNCTION vars_regenerate_update()
RETURNS TRIGGER AS $$
BEGIN
PERFORM generate_var_getter();
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_vars_regenerate_change
AFTER INSERT OR UPDATE ON vars
EXECUTE FUNCTION vars_regenerate_update();
Jetzt können Sie Ihre Variablen problemlos in einer Tabelle aufbewahren, erhalten aber auch einen unveränderlichen Zugriff auf diese Variablen. Das Beste aus beiden Welten:
INSERT INTO vars DEFAULT VALUES;
-- INSERT 0 1
SELECT var_get('zipcode')::INT;
-- 90210
UPDATE vars SET zipcode = 84111;
-- UPDATE 1
SELECT var_get('zipcode')::INT;
-- 84111