Wie Wert von composite-variable-Feld mit dynamic SQL
Angesichts dieser Typ:
-- Just for testing purposes:
CREATE TYPE testType as (name text)
Bekomme ich den Wert eines Feldes dynamisch mit dieser Funktion:
CREATE OR REPLACE FUNCTION get_field(object anyelement, field text) RETURNS text as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'SELECT $1."' || field || '"'
USING object
INTO value;
return value;
END;
$BODY$
LANGUAGE plpgsql
Aufrufen get_field('(david)'::testType, 'name')
funktioniert wie erwartet Rückkehr "david".
Aber wie kann ich einen Wert von einem Feld in ein composite-Typ? Ich habe versucht diese Funktionen:
CREATE OR REPLACE FUNCTION set_field_try1(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE '$1."' || field || '" := $2'
USING object, value;
return object;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION set_field_try2(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'SELECT $1 INTO $2."' || field || '"'
USING value, object;
return object;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION set_field_try3(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'BEGIN $1."' || field || '" := $2; SELECT $1; END;'
INTO object
USING value, object;
return object;
END;
$BODY$
LANGUAGE plpgsql
und einige Variationen.
Aufruf set_field_tryX
funktioniert nicht. Ich bekomme immer "FEHLER: syntax-Fehler an oder in der Nähe von...".
Wie kann ich dies erreichen?
Hinweise:
- Der parameter
anyelement
und das Feld kann ein beliebiges Feld in der composite-Typ. Ich kann nicht einfach mit dem Objekt.name. - Ich bin besorgt über SQL-injection. Jede Beratung in diesem würde geschätzt werden, aber es ist nicht meine Frage.
InformationsquelleAutor DavidEG | 2011-10-10
Du musst angemeldet sein, um einen Kommentar abzugeben.
Schneller mit
hstore
Da Postgres 9.0, mit der zusätzliche module
hstore
installiert, die in Ihrer Datenbank es gibt eine sehr einfache und schnelle Lösung mit der#=
- operator, dass ...Installieren Sie das Modul:
Beispiele:
Werte müssen Stimmen, um
text
und zurück, offensichtlich.Beispiel plpgsql-Funktionen mit mehr details:
Fast, als schnell mit
json
Gibt es ähnliches, aber derzeit ohne Papiere (S. 9.5) - Lösungen mit
json
(S. 9.3+) oderjsonb
(pg 9.4+), gebaut in der Postgres, so brauchen Sie nicht ein zusätzliches Modul.Siehe @Geir ' s hat die Antwort für details.
Ohne
hstore
undjson
Wenn Sie auf eine ältere version oder kann die Installation der zusätzlichen module
hstore
oder kann nicht davon ausgehen, es ist installiert, hier ist eine verbesserte version von dem, was ich geschrieben zuvor. Noch langsamer als diehstore
- operator, wenn:Nennen:
Notes
Einen expliziten cast des Wertes
_val
zu der Ziel-Datentyp ist nicht notwendig, ein string-literal in der dynamischen Abfrage wäre gezwungen automatisch, dass die Unterabfrage aufpg_type
. Aber ich nahm es einen Schritt weiter:Ersetzen
quote_literal(_val)
mit direkten Wert der Einfügung über dieUSING
- Klausel. Spart einen Funktionsaufruf und zwei Würfe, und sicherer ist es ohnehin.text
dazu gezwungen wird, um den Ziel-Typ automatisch in modernen PostgreSQL. (Nicht getestet mit Versionen vor 9.1.)array_to_string(ARRAY())
ist schneller alsstring_agg()
.Keine Variablen benötigt, keine
DECLARE
. Weniger Zuweisungen.Keine Unterabfrage in der dynamischen SQL.
($1).field
ist schneller.pg_typeof(_comp_val)::text::regclass
macht das gleiche wie
(SELECT typrelid FROM pg_catalog.pg_type WHERE oid = pg_typeof($1)::oid)
für gültige composite-Typen, nur schneller.
Diese Letzte änderung basiert auf der Annahme, dass
pg_type.typname
ist immer identisch mit dem damit verbundenenpg_class.relname
für registrierte composite-Typen, und die doppelte Besetzung kann ersetzen Sie die Unterabfrage. Ich lief in diesem test in einer großen Datenbank, um zu überprüfen, und es kam leer als erwartet:Den Einsatz eines
INOUT
- parameter entfällt die Notwendigkeit für eine expliziteRETURN
. Dies ist nur ein notational Verknüpfung. Pavel wird nicht, wie es ist, bevorzugt er eine expliziteRETURN
Aussage ...Alles zusammen ist dies fast doppelt so schnell als die Vorherige version.
Original (überholt) Antwort:
Das Ergebnis ist eine version, die ~ 2.25 mal schneller. Aber ich hätte wahrscheinlich es nicht getan haben, ohne Gebäude auf Pavel ' s zweite version.
Neben, dieser version vermeidet die meisten der casting zu text und zurück, indem Sie alles innerhalb einer einzigen Abfrage, so sollte es sein, viel weniger fehleranfällig ist.
Getestet mit PostgreSQL 9.0 und 9.1.
danke, das ist es Wert wie zehn upvotes, kommt von dir. 🙂
Danke @Erwin. Ich bin immer noch auf dieser. Ich habe festgestellt, dass Ihre Lösung schlägt mit dieser:
CREATE TYPE a as (a1 int); CREATE TYPE b as (b1 a); SELECT setfield3(null::b, 'b1', '(2)');
. In diesem Fall setfield2 von @Pavel arbeitet, aber setfield2 schlägt in einigen Fällen, in denen setfield3 funktioniert 😕PostgreSQL 9.1.0
guten Mikro-Optimierung. Ich habe nichts gegen INOUT-Variablen. Nur, dass ich wie eine explizite Verwendung der RETURN-Anweisung (wie ADA erfordert). Explizite Rückgabe-Wert durch die Verwendung der RETURN-Anweisung ist ein wenig mehr robust, um menschliche Fehler, aber irgendwo ist nicht möglich (PL/pgSQL).
InformationsquelleAutor Erwin Brandstetter
Ich schrieb eine zweite version von setfield Funktion. Es funktioniert auf postgres 9.1 ich habe nicht testen Sie es auf älteren Versionen. Es ist kein Wunder (aus performance Sicht), aber es ist robuster und etwa 8-mal schneller als die Vorherige.
Ich habe zwei bugs gefixed. Hoffe, das ist ok mit dir? 1) Keine quote_ident() für den Typ! Es würde zerspringen, pg 9.0 mit einem Typ in einem schema ("myschema.mytype"). 2) Double single quotes, auch! Plus, ersetzt
\'
mit''
zu vermeiden escape-Zeichenfolgen.Ich merke jetzt, dass der Typ muss ein
quote_ident()
oder es ist nicht sicher gegen SQLi. Also ich machte, die Sie Bearbeiten. Allerdings schlägt dies fehl mit eine schema-qualifiziert Typ. Sie müsstequote_ident()
schema und typename separat:"myschema"."mytype"
.Ich arbeite immer noch daran. Ich habe einen Fehler gefunden, da die Arten
CREATE TYPE a as (a1 int); CREATE TYPE b as (b1 a); CREATE TYPE c as (c1 b[]);
läuftSELECT setfield2(null::c, 'c1', '{"(\"(2)\")"}');
ausfällt.Ich bin nicht in der Lage, um diese Funktion aufrufen, die von einem trigger ausgelöst. Wenn ich es mithilfe einer Tabelle, wie vorgeschlagen, D. H.
NULL::table_name
bekomme ich einen Spalte "tableoid" not found in data type "table_name", wenn Sie die Funktion aufrufen. Irgendeine Idee? Ich bin mit postgresql v. 9.1.InformationsquelleAutor Pavel Stehule
UPDATE/Achtung: Erwin weist darauf hin, dass dies derzeit ohne Papiere, und die Handbuch zeigt an, dass es nicht möglich sein sollte, zu verändern, Datensätze auf diese Weise.
Verwenden hstore oder Pavel Lösung statt.
Diese einfache json basierte Lösung, die ist fast so schnell wie hstore und erfordert nur Postgres 9.3 oder neuer. Dies sollte eine gute option, wenn Sie nicht verwenden können, die hstore-Erweiterung, und der performance-Unterschied sollte vernachlässigbar sein. Benchmarks: https://stackoverflow.com/a/28673542/1914376
a) Wir können entweder tun es inline durch cast/concat. Json-Funktion erfordert Postgres 9.3:
b) oder inline mit Funktionen von Postgres 9.4.
Anmerkung: ich habe json_object(ARRAY[key,value]) da war er etwas schneller als json_build_object(Schlüssel,Wert):
Ausblenden der casting-details, die Sie verwenden können, a) eine Funktion, mit wenig Aufwand.
InformationsquelleAutor Geir Bostad
"SELECT INTO" außerhalb plpgsql (in dynamische SQL-Kontext) hat anderen Sinn, als Sie erwarten - Sie speichern ein Ergebnis der Abfrage Tabelle.
Änderung von Feld ist möglich, aber nicht einfach
Aber dieser code ist nicht sehr effektiv - ist nicht möglich, dies zu schreiben, auch in plpgsql. Hier finden Sie einige C-Bibliothek, das sollte tun.
Danke @Pavel. Obwohl, vielleicht ist es nicht das beste, es ist genug für mich. Jetzt habe ich aber ein anderes problem: die Spalte ist nicht immer
text
und wenn es versucht, zu aktualisieren, ich habecolumn "x" is of type real but expression is of type text
. Wie kann ich dynamisch gewirkt?es arbeiten in meiner 9.1 - die einfachste Lösung ist überladen setfield Funktion für doppelte Genauigkeit: CREATE or REPLACE FUNKTION der öffentlichkeit.setfield(ein anyelement, text, doble precision) GIBT anyelement LANGUAGE plpgsql ALS $Funktion$ begin create temp table aux select $1.*; execute "update aux-set' || quote_ident($2) || ' = ' || $3; wählen Sie in $1 * aux; drop table aux; return $1; end; $Funktion$
Im Laufe des Schreibens eine andere version lief ich umfangreiche tests auf postgres 9.0. Diese Funktion maxed out-shared memory auf einem test mit ein paar tausend Zeilen. Server hatte anständige Ressourcen. Das ist also nicht fit für den Einsatz. WARNUNG: out of shared memory KONTEXT: SQL-Anweisung "create temp table aux select $1.*" PL/pgSQL-Funktion "setfield" Linie 2 bei der SQL-Anweisung
InformationsquelleAutor Pavel Stehule
Test-setup und benchmarks v2
Erwin ermutigt, sich zu reproduzieren, seine benchmark in diesem thread (https://stackoverflow.com/a/7782839/1914376), so dass ich geändert, seinen code mit synthetischen Testdaten und Hinzugefügt, sowohl für die hstore-Lösung und die json-Lösung aus meiner Antwort (und eine json-Lösung von Pavel gefunden in einem anderen thread) Die benchmark wird nun als eine Abfrage, so dass es leichter zu erfassen, die Ergebnisse.
Testergebnisse auf 9.4.1, win32, i5-4300U
hstore
Lösung verfügbar ist, da 9.0 wie gesagt in meiner Antwort. 2. hstore-Funktion sollte noch schneller unter(anyelement, hstore)
, speichern redundanter wirft. Sie nicht wirklich benötigen eine Funktion überhaupt, nur der Ausdruckmy_record #= hsore(field, value)
. 3. Sie enthalten meine erste Antwort (meine benchmark ist veraltet), aber nicht der viel verbesserte späteren version in meiner Antwort.Ich der festen 9.1/9.0 Tippfehler, und ließ die hstore-Funktion. Whoups! Habe ich irgendwie verpasst die verbesserte Antwort in der Bank, es ist jetzt im Lieferumfang enthalten:). Ich habe auch json als inline-test. Es war interessant zu sehen, dass json_object(ARRAY[key,value]) ist schneller als json_build_object(Schlüssel,Wert)
Ausgezeichnete post. Sehr interessant. Noch eine Sache: Funktion Volatilität. Erwin 1, Erwin 2 und Pavel 2
STABLE
(aktualisiert meine alte Antwort, zu), Pavel3 und Geir 1IMMUTABLE
. Das könnte einen Unterschied machen - wenn nicht in diesem test, dann in den Kontext von komplexeren Abfragen.Ja, einige komplexe use-cases sollten in der Lage sein zu nutzen unveränderlich (Eingang gleich mehrere Male?) In diesem synthetischen benchmark ist die Funktion tatsächlich bekam doppelt so langsam mit
IMMUTABLE
.Langsamer mit
IMMUTABLE
, das ist seltsam. Welche? Hier ist eine aktuelle Frage, woIMMUTABLE
den Unterschied gemacht (in einem nicht-so-offensichtlichen Weise): stackoverflow.com/questions/28899042/...InformationsquelleAutor Geir Bostad
Update März 2015:
Weitgehend veraltet. Betrachten Sie die neue benchmark von @Geir mit schnelleren Varianten.
Test-setup und benchmarks
Nahm ich die drei Lösungen vorgestellt (Okt. 16th, 2011) und lief ein test auf PostgreSQL 9.0.
Sie finden das komplette setup unten. Nur test-Daten sind nicht enthalten, da ich einen real life-Datenbank (nicht synthetische Daten). Es ist alles gekapselt in einem eigenen schema für non-intrusive verwenden.
Möchte ich ermutige jeden, der will, zu reproduzieren, zu testen. Vielleicht mit postgres 9.1? Und Stell deine Ergebnisse hier? 🙂
Benchmarks
Lief ich die Abfragen ein paar mal den cache füllen. Die vorgestellten Ergebnisse sind die besten von insgesamt fünf Laufzeiten mit
EXPLAIN ANALYZE
.Rirst Runde mit 1000 Zeilen
Pavel erstes Prototyp-maxes shared memory mit mehr Zeilen.
Pavel 1: 2445.112 ms
Pavel 2: 263.753 ms
Erwin 1: 120.671 ms
Ein weiterer test mit 5183 Zeilen.
Pavel 2: 1327.429 ms
Erwin1: 588.691 ms
Vielen Dank, ich habe eine neue Lösung zusammen mit einem reproduzierbaren benchmark basiert auf dem code. Sollten wir "verschmelzen" die benchmark-Beiträge irgendwie zu machen ist es einfacher, für neue Leser? Vorschläge werden gerne angenommen=)
InformationsquelleAutor Erwin Brandstetter