sql >> Database teknologi >  >> RDS >> PostgreSQL

Videregivelse af kolonnenavne dynamisk for en postvariabel i PostgreSQL

Arbejder med dette dummybord

CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)

Først forenklede og rensede jeg dit eksempel:

  • Fjernet noget støj, der er irrelevant for spørgsmålet.

  • RETURNS SETOF void giver næppe mening. Jeg bruger RETURNS void i stedet.

  • Jeg bruger text i stedet for character varying , bare for nemhedens skyld.

  • Når du bruger dynamisk SQL, har du for at sikre mig mod SQL-injektion, bruger jeg format() med %I I dette tilfælde. Der er andre måder.

Det grundlæggende problem er, at SQL er meget rigid med typer og identifikatorer. Du arbejder med dynamisk tabel navn samt med dynamisk feltnavn på en post - en anonym optage i dit originale eksempel. Pl/pgSQL er ikke godt rustet til at håndtere dette. Postgres ved ikke, hvad der er inde i en anonym registrering. Først efter at du har tildelt posten til en velkendt type kan du henvise til individuelle felter.
Her er et nært beslægtet spørgsmål, der prøver at indstille et felt i en post med dynamisk navn:
Sådan indstilles værdien af ​​det sammensatte variabelfelt ved hjælp af dynamisk SQL

Grundlæggende funktion

CREATE OR REPLACE FUNCTION getrowdata1(table_name text, id int)
  RETURNS void AS
$func$ 
DECLARE
   srowdata record;
   reqfield text := 'my_num';   -- assigning at declaration time for convenience
   value    numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT * FROM %I WHERE id = $1', table_name)
USING  id
INTO   srowdata;

RAISE NOTICE 'srowdata: %', srowdata;

RAISE NOTICE 'srowdatadata.my_num: %', srowdata.my_num;

/* This does not work, even with dynamic SQL
EXECUTE format('SELECT ($1).%I', reqfield)
USING srowdata
INTO value;

RAISE NOTICE 'value: %', value;
*/

END
$func$ LANGUAGE plpgsql;

Ring til:

SELECT * from getrowdata1('foo', 1);

Den kommenterede del ville rejse en undtagelse:

kunne ikke identificere kolonne "my_num" i postdatatypen:SELECT * fromgetrowdata(1,'foo')

hstore

Du skal installere det ekstra modul hstore for det. Én gang pr. database med:

CREATE EXTENSION hstore;

Så kunne alt fungere sådan her:

CREATE OR REPLACE FUNCTION getrowdata2(table_name text, id int)
  RETURNS void AS
$func$ 
DECLARE
   hstoredata hstore;
   reqfield   text := 'my_num';
   value      numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT hstore(t) FROM %I t WHERE id = $1', table_name)
USING  id
INTO   hstoredata;

RAISE NOTICE 'hstoredata: %', hstoredata;

RAISE NOTICE 'hstoredata.my_num: %', hstoredata -> 'my_num';

value := hstoredata -> reqfield;

RAISE NOTICE 'value: %', value;

END
$func$ LANGUAGE plpgsql;

Ring til:

SELECT * from getrowdata2('foo', 1);

Polymorf type

Alternativ uden at installere yderligere moduler.

Da du vælger en hel række i din registreringsvariabel, er der en veldefineret type for det per definition. Brug det. Nøgleordet er polymorfe typer .

CREATE OR REPLACE FUNCTION getrowdata3(_tbl anyelement, id int)
  RETURNS void AS
$func$ 
DECLARE
   reqfield text := 'my_num';
   value    numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tbl))
USING  id
INTO   _tbl;

RAISE NOTICE '_tbl: %', _tbl;

RAISE NOTICE '_tbl.my_num: %', _tbl.my_num;

EXECUTE 'SELECT ($1).' || reqfield   -- requfield must be SQLi-safe or escape
USING _tbl
INTO  value;

RAISE NOTICE 'value: %', value;

END
$func$ LANGUAGE plpgsql;

Ring til:

SELECT * from getrowdata3(NULL::foo, 1);

-> SQLfiddle

  • Jeg (ab-)bruger inputparameteren _tbl for tre formål her:

    • Giver den veldefinerede type rekorden
    • Giver navnet i tabellen, automatisk skemakvalificeret
    • Virker som variabel.
  • Mere forklaring i dette relaterede svar (sidste kapitel):
    Refaktorer en PL/pgSQL-funktion for at returnere output fra forskellige SELECT-forespørgsler




  1. Hvad skal du kontrollere, hvis PostgreSQL-hukommelsesudnyttelsen er høj

  2. En restaurantleveringsdatamodel

  3. Sådan kortlægges PostgreSQL enum med JPA og Hibernate

  4. Installer MySQL på en Mac