sql >> Databáze >  >> RDS >> PostgreSQL

Dynamické předávání názvů sloupců pro proměnnou záznamu v PostgreSQL

Práce s touto falešnou tabulkou

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

Nejprve jsem váš příklad zjednodušil a vyčistil:

  • Byl odstraněn nějaký šum, který není pro otázku relevantní.

  • RETURNS SETOF void sotva dává smysl. Používám RETURNS void místo toho.

  • Používám text místo character varying , jen pro jednoduchost.

  • Při použití dynamického SQL máte pro ochranu před SQL injection používám format() s %I v tomto případě. Existují i ​​jiné způsoby.

Základní problém je, že SQL je velmi rigidní s typy a identifikátory. Pracujete s dynamickou tabulkou název a také název dynamického pole záznamu - anonymní záznam ve vašem původním příkladu. Pl/pgSQL není na to dobře vybaven. Postgres neví, co je uvnitř anonymní záznam. Teprve poté, co záznam přiřadíte k dobře známému typu můžete odkazovat na jednotlivá pole.
Zde je úzce související otázka, kterou se snažíte nastavit pole záznamu s dynamickým názvem:
Jak nastavit hodnotu pole složené proměnné pomocí dynamického SQL

Základní funkce

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;

Volejte:

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

Komentovaná část by vyvolala výjimku:

nemohl identifikovat sloupec "my_num" v datovém typu záznamu:SELECT * fromgetrowdata(1,'foo')

hstore

Je třeba nainstalovat přídavný modul hstore pro tohle. Jednou za databázi s:

CREATE EXTENSION hstore;

Pak by vše mohlo fungovat takto:

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;

Volejte:

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

Polymorfní typ

Alternativa bez instalace dalších modulů.

Protože do proměnné záznamu vyberete celý řádek, existuje dobře definovaný typ pro to podle definice. Použij to. Klíčovým slovem jsou polymorfní typy .

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;

Volejte:

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

-> SQLfiddle

  • (ab-)používám vstupní parametr _tbl za tři účely zde:

    • Poskytuje dobře definovaný typ záznamu
    • Poskytuje jméno tabulky, automaticky kvalifikováno podle schématu
    • Slouží jako proměnná.
  • Další vysvětlení v této související odpovědi (poslední kapitola):
    Refaktorujte funkci PL/pgSQL tak, aby vrátila výstup různých dotazů SELECT




  1. SQLException:Řetězec nebo binární data by byla zkrácena

  2. Jak získat aktuální čas (bez časového pásma) v PostgreSQL

  3. Jak se připojit k Oracle pomocí JRuby &JDBC

  4. ERROR 2003 (HY000):Nelze se připojit k serveru MySQL na '127.0.0.1' (111)