PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verständnis des Unterschieds zwischen int-Literal und int-Parameter in der PL/pgSQL-Funktion

Warum?

PL/pgSQL führt SQL-Abfragen wie vorbereitete Anweisungen aus . Das Handbuch zur Parameterersetzung:

Beachten Sie den Begriff Werte . Es können nur Istwerte parametriert werden, nicht aber Schlüsselwörter, Bezeichner oder Typnamen. 32 in bit(32) aussehen wie ein Wert, aber der Modifikator eines Datentyps ist intern nur ein "Wert" und kann nicht parametrisiert werden. SQL verlangt, Datentypen in der Planungsphase zu kennen, es kann nicht auf die Ausführungsphase warten.

Sie könnten Erreichen Sie Ihr Ziel mit dynamischem SQL und EXECUTE . Als Proof of Concept :

CREATE OR REPLACE FUNCTION lpad_bits(val varbit, sz int = 32, OUT outval varbit) AS
$func$
BEGIN
   EXECUTE format('SELECT $1::bit(%s) >> $2', sz)  -- literal
   USING val, sz - length(val)                     -- values
   INTO outval;
END
$func$  LANGUAGE plpgsql IMMUTABLE;

Aufruf:

SELECT lpad_bits(b'1001100111000', 32);  

Beachten Sie die Unterscheidung zwischen sz als literal verwendet um die Anweisung und ihr zweites Vorkommen zu erstellen, wo sie als Wert verwendet wird , der als Parameter übergeben werden kann.

Schnellere Alternativen

Eine überlegene Lösung für diese spezielle Aufgabe ist die einfache Verwendung von lpad() wie @Abelito vorgeschlagen :

CREATE OR REPLACE FUNCTION lpad_bits2(val varbit, sz int = 32)
  RETURNS varbit AS
$func$
SELECT lpad(val::text, sz, '0')::varbit;
$func$  LANGUAGE sql IMMUTABLE;

(Einfacher als einfache SQL-Funktion, die auch Funktions-Inlining erlaubt im Kontext äußerer Abfragen.)

Mehrfach schneller als die obige Funktion. Ein kleiner Fehler:Wir müssen in text umwandeln und zurück zu varbit . Leider lpad() ist derzeit nicht für varbit implementiert . Das Handbuch:

overlay() verfügbar ist, können wir eine billigere Funktion haben:

CREATE OR REPLACE FUNCTION lpad_bits3(val varbit, base varbit = '00000000000000000000000000000000')
  RETURNS varbit AS
$func$
SELECT overlay(base PLACING val FROM bit_length(base) - bit_length(val))
$func$  LANGUAGE sql IMMUTABLE;

Schneller, wenn Sie mit varbit arbeiten können Werte zu Beginn. (Der Vorteil entfällt (teilweise), wenn text gecastet werden muss zu varbit sowieso.)

Aufruf:

SELECT lpad_bits3(b'1001100111000', '00000000000000000000000000000000');
SELECT lpad_bits3(b'1001100111000',  repeat('0', 32)::varbit);

Wir könnten überladen die Funktion mit einer Variante, die eine ganze Zahl nimmt, um base zu generieren selbst:

CREATE OR REPLACE FUNCTION lpad_bits3(val varbit, sz int = 32)
  RETURNS varbit AS
$func$
SELECT overlay(repeat('0', sz)::varbit PLACING val FROM sz - bit_length(val))
$func$  LANGUAGE sql IMMUTABLE;

Aufruf:

SELECT lpad_bits3(b'1001100111000', 32;

Verwandte: