2015-12-01 31 views
6

W jaki sposób można użyć wyniku zapytania zapisanego w zmiennej typu rekordu dla innego zapytania w ramach tej samej funkcji zapisanej? Używam Postgres 9.4.4.Jak używać zmiennej typu rekord w plpgsql?

Z tabeli jak poniżej:

create table test (id int, tags text[]); 
insert into test values (1,'{a,b,c}'), 
         (2,'{c,d,e}'); 

napisałem funkcję (uproszczony) jak poniżej:

CREATE OR REPLACE FUNCTION func(_tbl regclass) 
RETURNS TABLE (t TEXT[], e TEXT[]) 
LANGUAGE plpgsql AS $$ 
DECLARE 
    t RECORD; 
    c INT; 
BEGIN 
    EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t; 
    SELECT count(*) FROM t INTO c; 
    RAISE NOTICE '% results', c; 
    SELECT * FROM t; 
END 
$$; 

... ale nie działa:

select func('test'); 
ERROR: 42P01: relation "t" does not exist 
LINE 1: SELECT count(*) FROM t 
          ^
QUERY: SELECT count(*) FROM t 
CONTEXT: PL/pgSQL function func(regclass) line 7 at SQL statement 
LOCATION: parserOpenTable, parse_relation.c:986 

Odpowiedz

15

Podstawowe nieporozumienie: zmienna record zawiera pojedynczy wiersz pojedynczy (lub ma wartość NULL), a nie tabelę (0-n wierszy znanego typu). Istnieje brak "zmiennych tabelarycznych" w Postgres lub PL/pgSQL. W zależności od zadania, istnieją różne możliwości:

W związku z tym nie można przypisać wielu wierszy do zmiennej record typu. W tym stwierdzeniu:

EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t; 

... Postgres przypisuje tylko pierwszy wiersz i odrzuca resztę. Ponieważ "pierwszy" nie jest dobrze zdefiniowany w zapytaniu, otrzymujesz arbitralny wybór. Oczywiście z powodu wspomnianego na początku nieporozumienia.

Zmienna A także nie może być używana zamiast tabel w zapytaniach SQL. To jest główną przyczyną błędu można uzyskać:

relacja „t” nie istnieje

To powinno być już jasne, że count(*) nie miałoby sensu, aby rozpocząć, ponieważ t to tylko jeden rekord/wiersz - poza tym, że i tak jest to niemożliwe.

Na koniec (nawet jeśli reszta zadziała), Twój typ zwrotu wydaje się nieprawidłowy: (t TEXT[], e TEXT[]) . Ponieważ wybrałeś id, tags na t, powinieneś zwrócić coś podobnego do (id int, e TEXT[]).

Co próbujesz zrobić będzie działać jak ten:

CREATE OR REPLACE FUNCTION func(_tbl regclass) 
    RETURNS TABLE (id int, e text[]) AS 
$func$ 
DECLARE 
    _ct int; 
BEGIN 
    EXECUTE format(
     'CREATE TEMP TABLE tmp ON COMMIT DROP AS 
     SELECT id, tags FROM %s' 
    , _tbl); 

    GET DIAGNOSTICS _ct = ROW_COUNT; -- cheaper than another count(*) 

    -- ANALYZE tmp; -- if you are going to run multiple queries 

    RAISE NOTICE '% results', _ct; 

    RETURN QUERY TABLE tmp; 
END 
$func$ LANGUAGE plpgsql; 

połączeń (nota składni!):

SELECT * FROM func('test'); 

Powiązane:

Just a dowód koncepcji. Podczas wybierania całej tabeli wystarczy użyć tabeli bazowej. W rzeczywistości będziesz mieć jakieś WHERE klauzuli w zapytaniu ...

Ostrożnie z niedopasowania typu Dostrzec count() powraca bigint, nie można przypisać do zmiennej integer. Potrzebował by obsady: count(*)::int.

Ale zastąpiony że całkowicie, jest to tańsze, aby uruchomić to prawo po EXECUTE:

GET DIAGNOSTICS _ct = ROW_COUNT; 

Details in the manual.

Dlaczego ANALYZE?


marginesie: CTE w czystym SQL często może wykonać zadanie:

+0

Dziękuję bardzo, Erwin, dla rodzaju i szczegółowa odpowiedź. Powód jest dla mnie bardzo jasny. Będę używał CTE do tego celu. –