Temat: Jak sprawdzić w funkcji czy w bazie jest dana tabela
Witam!
Mam funkcję w której muszę sprawdzić czy dana tabela jest w bazie z tym że większość tabel ma nazwy złożone z liczb. Chyba istnieje jakaś standardowa funkcja do tego sprawdzenia.
PostgreSQL to najbardziej zaawansowany system relacyjnych baz danych Open Source.
Nie jesteś zalogowany. Proszę się zalogować lub zarejestrować.
Forum PostgreSQL » PostgreSQL dla początkujących » Jak sprawdzić w funkcji czy w bazie jest dana tabela
Strony 1
Zaloguj się lub zarejestruj by napisać odpowiedź
Witam!
Mam funkcję w której muszę sprawdzić czy dana tabela jest w bazie z tym że większość tabel ma nazwy złożone z liczb. Chyba istnieje jakaś standardowa funkcja do tego sprawdzenia.
Jest funkcja pg_is_table_visible(oid), ktora sprawdza czy mozesz sie odwolac do tabeli bez podania prefixu schematu w którym została utworzona. O schematach w postgresie mozesz poczytac tu (http://www.postgresql.org/docs/8.3/interactive/sql-createschema.html, ale ogólnie chodzi o to ze mozesz sobie podzielic baze na 'katalogi' kazdy ze swoimi tabelami (cos a'la system plikow gdzie schemat=katalog, tabela=plik). Jest taka zmienna, search_path, w ktorej możesz ustawić jakie schematy maja byc przegladane gdy wykonujesz jakies operacje na tabelach (cos jak zmienna $PATH w linuxie). Funkcja pg_is_table_visible(oid) dziala mniej wiecej tak (wykorzystujac Twoja wczesniejsza funkcje)
>select f_sint_CreateTable_tbl(113::smallint);
f_sint_createtable_tbl
------------------------
(1 row)
>select pg_table_is_visible(('113'::varchar)::regclass);
pg_table_is_visible
---------------------
t
(1 row)
(rzutowanie jest niezbedne, nie da sie chyba zrzutowac z integer na oid stad posrednie rzutowanie na varchar)
Czyli dostajesz 't' jesli tabela jest widoczna dla twoich polecen bez potrzeby prefixowania jej. Ale jesli dostaniesz 'f' to nie znaczy ze tabeli nie ma, moze istniec w schemacie, ktorego nie ma w search_path. Ale jesli nie tworzyles swoich schematow to znaczy ze uzywasz tylko jednego domyslnego o nazwie public. Ten schemat jest domyślnie ustawiony w zmiennej search_path więc funkcja o ktora wspominam spokojnie ci wystarczy i bedzie dzialac.
Mozesz tez napisac wlasna funkcje, ktora bedzie przegladac tabele systemowa pg_tables i szukac czy jakas tabela (np przekazana jej jako argument) juz istnieje.
Ale sie napisalem . Mam nadzieje ze cos wyjasnilem
Sprawdzałem chyba powinno zadziałać tylko nie wiem jak mam tego użyć w mojej funkcji chodzi o to żeby sprawdzić czy dana tabela istnieje jeżeli nie to utworzyć jeżeli tak to wpisać do niej dane. W zmiennej Pojazd jest numer tabeli który ma być jej nazwą. I chyba nie przejdzie składnia INSERT INTO Pojazd (....).
CREATE OR REPLACE FUNCTION f_tri_aktual_mt_read() RETURNS trigger AS
$$
DECLARE
licz INTEGER DEFAULT 0;
dg timestamp without time zone;
val INTEGER;
AwariaSkrot SMALLINT;
Pojazd SMALLINT;
BitInfo SMALLINT;
BEGIN
dg := NEW.mt_time;
val := NEW.mt_value;
AwariaSkrot := f_txt_AwariaSkrot_sint(NEW.mt_name);
Pojazd := f_txt_Pojazd_int(NEW.mt_name);
BitInfo := f_txt_BitInfo_sint(NEW.mt_name);
IF (tabela Pojazd nie istnieje) THEN Utworzyc tabele Pojazd END IF;
IF (BitInfo = 0) THEN
INSERT INTO Pojazd ("dtDataGodzina", "idfkAwarieSkroty", "sintNrBitu", "intWartosc")
VALUES (dg, AwariaSkrot, 0, val);
RETURN NEW;
END IF;
IF (BitInfo = 16) THEN
WHILE licz < 16
LOOP
IF ((val & (1<<licz))<>0) THEN
INSERT INTO Pojazd ("dtDataGodzina", "idfkAwarieGrupy", "sintNrBitu", "sintWartosc")
VALUES (dg, AwariaSkrot, licz + 1, val & (1<<licz));
END IF;
licz=licz+1;
END LOOP;
RETURN NEW;
END IF;
END;
$$
LANGUAGE PLPGSQL;
Ostatnio edytowany przez WitekS (2009-03-03 09:02:27)
a
execute 'INSERT INTO "'||Pojazd||'" ("dtDataGodzina", "idfkAwarieSkroty", "sintNrBitu", "intWartosc")
VALUES (dg, AwariaSkrot, 0, val)';
nie dziala?
Ostatnio edytowany przez rski (2009-03-03 19:01:52)
rzeczywiście składnia podobna do poprzedniej to pewnie zadziała tylko jak poprawnie sformułować ten kod:
IF (tabela Pojazd nie istnieje)
THEN Utworzyc tabele Pojazd
END IF;
If' mozesz zaimplementowac tak
CREATE OR REPLACE FUNCTION ex(smallint)
RETURNS varchar AS $$
DECLARE
str varchar;
begin
select into str tablename from pg_tables where tablename="$1";
if not found then
-- TABELA NIE ISTNIEJE
else
--TABELA ISTNIEJE
end if;
end;
$$ LANGUAGE plpgsql;
Wszystko OK tylko nazwę tabeli mam w zmiennej Pojazd i jest typu INTEGER chyba powinno być tak
SELECT INTO str tablename FROM pg_tables WHERE tablename='"'||Pojazd||'"';
ale nie jestem pewny bo nie znam tych składni PL/pgSQL a dopiero zaczynam z PostgreSQL-em stąd moja prośba czy możesz nieco nakreślić działanie tego sprawdzenia? pg_tables to widok w którym są przechowywane informacje o tabelach w bazie w danym shemacie, $1 odwołuje się do parametru funkcji ex(smallint) a jakie zadanie ma zmienna str która chyba bez przypisania wartości ma wartość NULL???
W przypadku gdy tabela nie istnieje chciałbym ją utworzyć przez funkcję którą wcześniej napisałem
f_int_CreateTable_tbl(Pojazd);
ale chyba musi to być inaczej napisane???
Ostatnio edytowany przez WitekS (2009-03-04 08:56:45)
Tak pg_tables to widok z tabeliami w bazie. $1 jak mówisz to pierwszy argument wywołania funkcji. W fukcjach plpgsql nie powinno się używać select'ów, które nigdzie nie zapisują swojego wyniku. Dlatego tu sztucznie wprowadziłem zmienną str aby przypisać jej nazwę tabeli jeśli istnieje. Zamiast sprawdzać czy str jest różne od null, możesz użyć składni "if not found" (lub "if found"), która przechowuje info o tym czy ostatnie wykonane zapytanie (ostatnie wykonane przed "if not found") coś zwróciło lub nie.
Skrypt może wyglądać następująco
CREATE OR REPLACE FUNCTION ex(smallint)
RETURNS varchar AS $$
DECLARE
str varchar;
begin
select into str tablename from pg_tables where tablename="$1";
if not found then
perform f_sint_CreateTable_tbl($1);
return 'created';
else
return 'already exists';
end if;
end;
$$ LANGUAGE plpgsql;
Użyty perform to taki select, w którym nie interesuje nas zwracana wartość, dlatego nie trzeba jej nigdzie zapisywać. Tutaj interesuje nas wyłącznie utworzenie tabeli wiec dlatego użyłem perform.
Ponieważ funkcja powinna coś zwracać więc i w if i else jest "return". Równie dobrze mógłbyć jeden return na końcu funkcji (poza if/else).
Funkcja ex(..) zadziała oczywiście tylko wtedy gdy będzie juz utworzona funkcja f_sint_CreateTable_tbl(..)
Mam nadzieje, że to Ci pomoże.
Mam coś takiego:
CREATE OR REPLACE FUNCTION f_tri_ai_aktual_mt_read() RETURNS trigger AS
$$
DECLARE
Pojazd INTEGER;
str VARCHAR;
BEGIN
Pojazd := f_txt_Pojazd_int(NEW.mt_name);
SELECT INTO str tablename FROM pg_tables WHERE tablename='"'||Pojazd||'"';
IF not found THEN
PERFORM f_sint_CreateTable_tbl(Pojazd);
RETURN 'created';
ELSE
RETURN 'already exists';
END IF;
END;
$$
LANGUAGE PLPGSQL;
No i dostaje taki komunikat:
Use CREATE LANGUAGE to load the language into the database.
Coś mam nie tak tylko co?
createlang plpgsql nazwa_bazy
Używam windows xp a wcześniej wszystkie inne funkcje działały ;( link który podałeś nie dział więc spróbuję coś znaleźć na temat dodawania języka proceduralnego chociaż wydawało mi się że zaznaczyłem plpgsql przy instalacji...
select * from pg_language;
Język jest w bazie zainstalowany w zasadzie nic nie zmieniałem a teraz przy próbie zapisania funkcji:
CREATE OR REPLACE FUNCTION f_tri_ai_aktual_mt_read() RETURNS trigger AS
$$
DECLARE
Pojazd INTEGER;
str VARCHAR;
BEGIN
Pojazd := f_txt_Pojazd_int(NEW.mt_name);
SELECT INTO str tablename FROM pg_tables WHERE tablename='"'||Pojazd||'"';
IF not found THEN
PERFORM f_sint_CreateTable_tbl(Pojazd);
RETURN 'created';
ELSE
RETURN 'already exists';
END IF;
END;
$$
LANGUAGE PLPGSQL;
mam coś takiego
ERROR: RETURN must specify a record or row variable in function returning tuple at or near "'created'"
LINE 19: RETURN 'created';
^
********** Błąd **********
Ostatnio edytowany przez WitekS (2009-03-10 08:52:20)
Przy return's trigger musisz zwracac
return new;
lub
return old;
lub
return null;
Czemu masz returns trigger?
returns trigger bo muszę sprawdzić przy wstawieniu danych do tabeli mt_read do jakiej tabeli mam wpisać zmodyfikowany rekord i jeżeli taka tabela nie istnieje to utworzyć, nazwa tabeli jest numerem zwracanym na podstawie wpisywanego do mt_read rekordu przez funkcję:
f_txt_Pojazd_int(NEW.mt_name);
Czyli z tego rozumiem że zamiast RETURN 'created'; oraz RETURN 'already exists'; muszę napisać return null???;
Sprawdziłem działanie no i jest tak że jeżeli tabela nie istnieje to jest utworzona przez funkcję jednak jeżeli już jest to mam taki błąd:
ERROR: relation "3111" already exists
KONTEKST: SQL statement " CREATE TABLE "3111"(
dtDataGodzina timestamp(0) without time zone NOT NULL,
idfkAwarieSkroty smallint NOT NULL,
sintNrBitu smallint NOT NULL,
intWartosc integer
) WITH OIDS"
PL/pgSQL function "f_int_createtable_tbl" line 5 at EXECUTE statement
SQL statement "SELECT f_int_CreateTable_tbl( $1 )"
PL/pgSQL function "f_tri_ai_aktual_mt_read" line 22 at PERFORM
Ostatnio edytowany przez WitekS (2009-03-10 15:06:25)
To pokaż jak teraz wyglada cała funkcja.
To jest funkcja wywoływana po wstawieniu rekordu do mt_read a po przeanalizowaniu z tego rekordu mają być wpisywane (jeden bądź kilka w zależności od wartości) do tabel tworzonych jeżeli nie istnieją o nazwie zwracanej przez funkcję.
-- Function: f_tri_ai_aktual_mt_read()
-- DROP FUNCTION f_tri_ai_aktual_mt_read();
CREATE OR REPLACE FUNCTION f_tri_ai_aktual_mt_read()
RETURNS trigger AS
$BODY$
DECLARE
licz INTEGER DEFAULT 0;
dg timestamp without time zone;
val INTEGER;
AwariaSkrot SMALLINT;
Pojazd INTEGER;
BitInfo SMALLINT;
str VARCHAR;
BEGIN
dg := NEW.mt_time;
val := NEW.mt_value;
AwariaSkrot := f_txt_AwariaSkrot_sint(NEW.mt_name);
Pojazd := f_txt_Pojazd_int(NEW.mt_name);
BitInfo := f_txt_BitInfo_sint(NEW.mt_name);
SELECT INTO str tablename FROM pg_tables WHERE tablename='"'||Pojazd||'"';
IF not found THEN
PERFORM f_int_CreateTable_tbl(Pojazd);
RETURN null;
ELSE
RETURN null;
END IF;
IF (BitInfo = 0) THEN
EXECUTE 'INSERT INTO "'||Pojazd||'" ("dtDataGodzina", "idfkAwarieSkroty", "sintNrBitu", "intWartosc")
VALUES (dg, AwariaSkrot, 0, val)';
RETURN NEW;
END IF;
IF (BitInfo = 16) THEN
WHILE licz < 16
LOOP
IF ((val & (1<<licz))<>0) THEN
EXECUTE 'INSERT INTO "'||Pojazd||'" ("dtDataGodzina", "idfkAwarieGrupy", "sintNrBitu", "sintWartosc")
VALUES (dg, AwariaSkrot, licz + 1, val & (1<<licz))';
END IF;
licz=licz+1;
END LOOP;
RETURN NEW;
END IF;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
ALTER FUNCTION f_tri_ai_aktual_mt_read() OWNER TO postgres;
Dziwna troche ta funkcja. Musisz pamietac ze return powoduje wyjscie z funkcji. Jak masz w if'ie i else 'return null' to juz do tego 'return new' na koncu funkcji raczej nie dojdzie. Jestes pewien ze dobrze przemyslales dzialanie tej funkcji?
Nie wiem czy dziwna funkcja... nie znam postgres-a ani też składni plpgsql. Wcześniej miałem wszystko na MySQL-u i działało tak jak miało działać. W takim razie jak można wykonać takie zadanie. Przychodzi wpis do bazy do tabeli mt_read:
1. muszę sprawdzić są wartości do wpisania i wykonać następne działania np. utworzyć tabelę jeżeli nie istnieje (to zadanie - sprawdzenie i utworzenie muszę chyba przenieść całkowicie do funkcji tworzącej tabele???)
2. w zależności od wartości liczby wpisywanej do mt_read muszę rozbić jeden wpis na kilka rekordów i wpisać je do tabeli utworzonej wcześniej która nazwa jest wyznaczona przez funkcję...
Jeżeli funkcja wyzwalacza może zwracać tylko trzy wartości new, old, null a po null kończy działanie to nie wiem jak mam wykonać ten wyzwalacz...
Ostatnio edytowany przez WitekS (2009-03-11 10:42:03)
Posty [ 19 ]
Strony 1
Zaloguj się lub zarejestruj by napisać odpowiedź
[ Wygenerowano w 0.008 sekund, wykonano 10 zapytań ]