ERROR: relation with OID ... does not exist

Hallo,

z.Z. arbeite ich mich in PostgreSQL ein und versuche eine kleine Datenbank, die bisher unter MySql lief, auf PostgreSQL umzuschreiben.

Installiert ist die aktuelle Windows-Version von PostgreSQL unter WinXP.

Im wesentlichen geht es um die 3 nachfolgenden Funktionen:


CREATE OR REPLACE FUNCTION create_table_daten_afa_anlagen() RETURNS void AS $BODY$

DECLARE
v_count integer;

BEGIN

SELECT INTO v_count count(*) FROM pg_tables where tablename = ‘daten_afa_anlagen’;

IF (v_count <> 0) THEN

TRUNCATE TABLE daten_afa_anlagen;

ELSE

CREATE TABLE daten_afa_anlagen
(
Inventarnummer VARCHAR(20) NULL,
Bezeichnung VARCHAR(100) NULL,
Anschaffungsdatum VARCHAR(10) NULL,
Anschaffungskosten NUMERIC (12, 2) NULL,
Buchwert NUMERIC (12, 2) NULL,
Nutzungsdauer INTEGER NULL,
Anlagekonto INTEGER NULL,
AfaArt VARCHAR(25) NULL,
AfaProcent INTEGER NULL,
time_created timestamp null
);

COMMENT ON TABLE daten_afa_anlagen IS ‘Daten für die Anlagen-Verwaltung’;

END IF;

END;

$BODY$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION do_daten_afa_anlagen() RETURNS void AS $BODY$

DECLARE
rec RECORD;
last_konto integer := 0;



BEGIN

PERFORM create_table_daten_afa_anlagen ();


INSERT INTO daten_afa_anlagen
(
Anschaffungsdatum,
Bezeichnung,
Anschaffungskosten,
Anlagekonto,
time_created
)
SELECT buchungsdaten.belegdatum AS anschaffungsdatum,
buchungsdaten.buchungstext AS bezeichnung,
round (buchungsdaten.buchungsbetrag/(1+kontoplan.ust), 2) as anschaffungskosten,
buchungsdaten.sollkonto as anlagekonto,
now()
FROM buchungsdaten
INNER JOIN konten_anlagen
ON buchungsdaten.sollkonto = konten_anlagen.sollkonto
INNER JOIN kontoplan
ON buchungsdaten.sollkonto = kontoplan.nummer
WHERE lower(buchungsdaten.buchungstext) <> ‘eb-wert’;

END;

$BODY$
LANGUAGE plpgsql;

COMMENT ON FUNCTION do_daten_afa_anlagen() IS ‘Erstellt Buchungssätze für die Anlagenverwaltung’;


CREATE OR REPLACE FUNCTION run_first() RETURNS void AS $BODY$

BEGIN

PERFORM … ; – hier werden noch andere Funktion aufgerufen
PERFORM … ; – hier werden noch andere Funktion aufgerufen
PERFORM … ; – hier werden noch andere Funktion aufgerufen

PERFORM do_daten_afa_anlagen ();


END;

$BODY$
LANGUAGE plpgsql;


Das Problem:

Nachdem ich die Funktionen in die Datenbank eingespielt habe, rufe ich mit dem Abfragetool von pgAdmin III die Funktion run_first (select run_first() :wink: auf. Beim ersten Durchlauf ist alles OK. Starte ich direkt danach nochmals die Funktion erhalte ich folgende Fehlermeldung:


ERROR: relation with OID 46236 does not exist
KONTEXT: SQL statement “INSERT INTO daten_afa_anlagen ( Anschaffungsdatum, Bezeichnung, Anschaffungskosten, Anlagekonto, time_created ) SELECT buchungsdaten.belegdatum AS anschaffungsdatum, buchungsdaten.buchungstext AS bezeichnung, round (buchungsdaten.buchungsbetrag/(1+kontoplan.ust), 2) as anschaffungskosten, buchungsdaten.sollkonto as anlagekonto, now() FROM buchungsdaten INNER JOIN konten_anlagen ON buchungsdaten.sollkonto = konten_anlagen.sollkonto INNER JOIN kontoplan ON buchungsdaten.sollkonto = kontoplan.nummer WHERE lower(buchungsdaten.buchungstext) <> ‘eb-wert’”
PL/pgSQL function “do_daten_afa_anlagen” line 13 at SQL statement
SQL statement “SELECT do_daten_afa_anlagen ()”
PL/pgSQL function “run_first” line 47 at perform


Was ist die Ursache für diese Fehlermeldung?
Welchen Fehler habe ich in den erstellten Funktionen eingebaut, den ich nicht sehe?



mfg

Friedrich Stockebrand

hi.

also ich kann hier irgendwie auch nicht wirklich einen fehler feststellen. “relation with OID 46236 does not exist” würde ja heißen, das eine beziehung nicht existiert. aber komisch ist, das das ganze beim ersten aufruf funktioniert…

würde ein 3. oder 4. aufruf klappen oder bekommst du da den gleichen fehler?

cu tb.

Hallo, ich habe ebenfalls das problem und keine loesung! die funktion funktioniert nur beim ersten mal nach dem laden, danach nie wieder ohne genannte fehlermeldung (wer will schon ständig ‘\i…sql’). mein beitrag steht unter plpgsql probleme. Hat denn niemand eine loesung fuer diesen mist?

gruss,

sago.

aus faq postgres.org:

4.19) Why do I get “relation with OID ##### does not exist” errors when accessing temporary tables in PL/PgSQL functions?
PL/PgSQL caches function scripts, and an unfortunate side effect is that if a PL/PgSQL function accesses a temporary table, and that table is later dropped and recreated, and the function called again, the function will fail because the cached function contents still point to the old temporary table. The solution is to use EXECUTE for temporary table access in PL/PgSQL. This will cause the query to be reparsed every time.

hi,

hab auf der postgresql seite folgendes gefunden:

4.19) Why do I get “relation with OID ##### does not exist” errors when accessing temporary tables in PL/PgSQL functions?

PL/PgSQL caches function scripts, and an unfortunate side effect is that if a PL/PgSQL function accesses a temporary table, and that table is later dropped and recreated, and the function called again, the function will fail because the cached function contents still point to the old temporary table. The solution is to use EXECUTE for temporary table access in PL/PgSQL. This will cause the query to be reparsed every time.

quelle: http://www.postgresql.org/docs/faqs.FAQ.html#item4.19 (punkt 4.19)

vielleicht hilft dir das weiter…

cu tb.

nachtrag: @sage du warst ein bisschen schneller wie ich :wink:

Hallo sago,

funktioniert es bei Dir mit “EXECUTE”?



mfg

fristo

Hallo Fristo,

so lala. muss ganz schöne verrenkungen machen, da EXECUTE in Kombination mit SELECT INTO nicht implementiert ist und seltsamerweise
EXECUTE ‘SELECT COUNT(*) FROM tbl_basis’ INTO int_cnt;
nicht klappt. Weißt du warum?
ansonsten ich baue mir ein string zusammen, nachdem ich vorher eine zweite temp-tabelle für die ergebnisse der zaehlungen aufgemacht habe, ziemlich von hinten durch die augen, oder?

vch_t_insert = ‘INSERT INTO test(cnt) SELECT COUNT(*) FROM tbl_basis’;

EXECUTE vch_t_insert;

Mit concat. || kann ich noch Variable übergeben.


Gruss,

sago

Hallo sago

Genau, deswegen habe ich nachgefragt. Ich musste auch so meine Erfahrung machen. Und das Zitat aus der FAQ ist leider auch nur die halbe Wahrheit.

Es funktioniert nicht nur EXECUTE in Kombination mit SELECT INTO nicht, sondern auch alle SQL-Befehle, die eine Variable, die man mit DECLARE definiert hat, enthalten. EXECUTE funktioniert also nur mit reinen SQL-Befehlen. Eine Ausnahme gibt es:

FOR record_oder_zeile IN EXECUTE text_ausdruck LOOP
anweisungen
END LOOP]… und seltsamerweise
EXECUTE ‘SELECT COUNT(*) FROM tbl_basis’ INTO int_cnt;
nicht klappt. Weißt du warum?[/quote]
Das ist nicht seltsam, schau Dir mal den Syntax vom SELECT-Befehl an!



Schau Dir das nachfolgende Konstrukt mal an, vielleicht kann es Dir helfen:

================================================================================


CREATE OR REPLACE FUNCTION create_table_tmp_XXXXX()
RETURNS void

AS $BODY$

BEGIN

IF (exists_table (‘tmp_XXXXX’)) THEN

TRUNCATE TABLE tmp_XXXXX;

ELSE

CREATE TABLE tmp_XXXXX
(

);

END IF;

END;

$BODY$

LANGUAGE PLPGSQL;

\



CREATE OR REPLACE FUNCTION exists_table(nameTable name)
RETURNS boolean

AS $BODY$

DECLARE
v_count integer;

BEGIN

SELECT INTO v_count
count(*)
FROM pg_tables
WHERE (
pg_tables.schemaname = current_schema()
AND
pg_tables.tablename = nameTable
);

RETURN (v_count <> 0);

END;

$BODY$

LANGUAGE ‘PLPGSQL’

\



CREATE OR REPLACE FUNCTION meine_function()
RETURNS …

AS $BODY$

BEGIN

PERFORM create_table_tmp_XXXXX ();




PERFORM create_table_tmp_XXXXX (); – löschen des Inhaltes.

END;

$BODY$

LANGUAGE PLPGSQL;

\

Was Deine Tabelle betrifft, dürftes Du keine Verenkungen mit EXECUTE mehr machen müssen. Es bleiben alle OID’s immer erhalten.


mfg

fristo

Hallo Fristo,

vielleicht durchschaue ich nicht, was Du genau mit dem Hinweis nach dem SELECT Syntax meinst,
hier mein Bezug für das ‘seltsame’ (nicht-)Verhalten von:

EXECUTE command-string [ INTO target ];

also:
[…]
DECLARE
int_cnt integer;
[…]
CREATE TEMPORARY TABLE t_a […];

vch_exec_count = ‘SELECT COUNT(*) FROM t_a’;

EXECUTE vch_exec_count INTO int_cnt;

klappt nicht. Ich hatte erwartet, dass das Aggregat in int_cnt geschrieben wird.
Ich vermute, dass es daran liegt, dass SELECT dem Count-Wert einen Bezeichner mitgibt,
also nicht den blanken Wert zurückgibt.

etwas an ‘EXECUTE command-string’ übergeben funktioniert dagegen
(leider nicht, wie von mir erwartet):

[…]
FOR rec_rt IN SELECT […]
LOOP
vch_exec_insert = 'INSERT INTO t_a (t1, t2, t3) SELECT ac1, ac2, ac3 FROM tbl_ac WHERE ac_rt_no = ';
vch_exec_insert := vch_exec_insert||rec_rt.rt_no;

EXECUTE vch_exec_insert;

END LOOP;

[…]


Es wird zwar immer berichtet,
das man große Datenmengen mit der Datenbank verarbeitet,
was ich auch nicht abstreiten will, aber komplexe Strukturen können das auch nicht sein.
Um eben mal eine plpgsql-Funktion schreiben, in der man temporäre Tabellen anlegt,
man plpgsql mal so richtig ausnutzen kann, und dann wieder die temporären Tabellen
mittel DROP zu löschen, kann ich mich nach meiner bisherigen Erfahrungen auch nicht vorstellen.
Ein Vorteil gegenüber MySQL sehe ich z. Z. nicht. Mit der neuen Version von MySQL habe ich
bisher nicht gearbeitet, so dass ich keine Aussage machen kann,
wie dort die “Stored Procedures” arbeiten. Aber general stellt PostgrSQL in
dieser Version kein Konkurrent zu MySQL dar.

Meine pg-db verarbeitet bis zu 10 mio rows (Mustervergleiche, Aggregate) in akzeptabler Zeit,
nun bin ich dabei, einige - sehr seriell verfasste - Funktionen umzuschreiben und weiter zu optimieren
(scan-kosten reduzieren), dabei stosse ich vermehrt auf etwas dumme Restriktionen, bspw. dieser OID-Mist.

Mit MySQL habe ich noch nicht gearbeit, da m.W. stored procedures bis zu neuesten Version nicht implementiert waren.
Zu postgresql bin ich über informix gekommen, und eigentlich ganz zufrieden gewesen. vacuum nervt.

aber: nix kosten und functions, triggers, views, eigene datentypen, schemata und interessante sachen wie pit-recovery.

deine Funktionsbeispiele schaue ich mir mal an. Danke!

Gruss,

sago

Hallo sago,

Du must den kleinen Unterschied sehen. Du hast mittels DECLARE die Varialbe int_cnt definiert. Um etwas in die Variable hinein zu Schreiben, muss du folgenden Code verwenden;

SELECT INTO int_cnt
count (*)
FROM t_a;


Die von Dir verwendete Form kann daher schon nicht funktionieren:

vch_exec_count = ‘SELECT COUNT(*) FROM t_a’;
EXECUTE vch_exec_count INTO int_cnt;

Aufgelöst: EXECUTE SELECT COUNT(*) FROM t_a INTO int_cnt;


Und damit sind wir wieder bei dem Problempunkt, der da heißt: “SELECT INTO wird innerhalb von EXECUTE gegenwärtig nicht unterstützt”. Es bleibt Dir daher nichts anderes übrig wie die Tablle t_a statisch anzulegen.



Was mich persönlich im Augenblick frustriert, kann Du hier nachlesen: http://www.pg-forum.de/ftopic48.html. Ich werde da auch noch einiges weitere schreiben.

Es geht mir nicht um die Anzahl der Datensätze, sondern es sind, wie Du schon selbst festgestellt hast, die Restriktionen. Dabei sehe ich 3 Problempunkte:

  1. Dynamische Befehle ausführen, oder genauer “SELECT INTO wird innerhalb von EXECUTE gegenwärtig nicht unterstützt”. Hier sollte es auch möglich sein, dass der Paser selbst feststellt, ob Befehle dynamisch ausgeführt werden müssen oder nicht, so das man auf die Form EXECUTE ’ … ’ verzichten könnte.

  2. OID

  3. Fehlende Exceptionverabeitung


    Wenn diese Problempunkte beseigtig werden, dürfte ProgreSQL eine Datenbank von der feinsten Art sein.


nix kosten? Die Datenbank, ja, und was kostet der zusätzliche Zeitaufwand für die Restritionen? Man muss das von verschiedenen Seiten betrachten.



mfg

fristo

Hallo fristo,

Deine Ausführungen zu der Konstruktion

EXECUTE command-string INTO target

könnten korrekt sein, müssen aber nicht, sofern man der built-in Dokumentation von pgadminIII zu ‘postgresql 8.1.0’ folgt:

Oftentimes you will want to generate dynamic commands inside your PL/pgSQL functions, that is, commands that will involve different tables or different data types each time they are executed. PL/pgSQL’s normal attempts to cache plans for commands will not work in such scenarios. To handle this sort of problem, the EXECUTE statement is provided:

EXECUTE command-string [ INTO target ];
where command-string is an expression yielding a string (of type text) containing the command to be executed and target is a record variable, row variable, or a comma-separated list of simple variables and record/row fields.

Note in particular that no substitution of PL/pgSQL variables is done on the command string. The values of variables must be inserted in the command string as it is constructed.

Unlike all other commands in PL/pgSQL, a command run by an EXECUTE statement is not prepared and saved just once during the life of the session. Instead, the command is prepared each time the statement is run. The command string can be dynamically created within the function to perform actions on different tables and columns.

The INTO clause specifies where the results of a SELECT command should be assigned. If a row or variable list is provided, it must exactly match the structure of the results produced by the SELECT (when a record variable is used, it will configure itself to match the result’s structure automatically). If multiple rows are returned, only the first will be assigned to the INTO variable. If no rows are returned, NULL is assigned to the INTO variable. If no INTO clause is specified, the results of a SELECT command are discarded.

SELECT INTO is not currently supported within EXECUTE.

(Kapitel 36.6. Basic Statements / 36.6.5. Executing Dynamic Commands)

Ich verstehe das so, dass command-string ‘gekapselt’ ausgeführt werden sollte und das Ergebnis in eine Zielvariable geschrieben werden kann.

Du hast recht, es funktioniert nicht mit einem command_string = “SELECT COUNT(*) FROM tbl_a” und ich halte das für einen bug, der hoffentlich bald behoben werden wird.

Zu meinem Problem:

Da ich ‘SELECT INTO’ durch ‘UPDATE’ ersetzen konnte, da ich den Wert eh in eine statische Tabelle schreiben muss, konnte ich die EXECUTE Variante in Verbindung mit TEMPORARY TABLE verwenden. Mir ist klar, dass ich letztendlich das Problem nur verschoben habe, da - falls ich diesen Wert innerhalb der Funktion ein weiteres Mal benötige, ich diesen wieder aus der stat. Tabelle auslesen muss - Im Zuge eines Optimierungsvorhabens bezüglich read/write/scan Kosten natürlich Asche.

Wenn man auf ein ‘SELECT INTO’, bzw. ‘EXECUTE varchar_select INTO target’ verzichten kann, klappt es zumindest, variable Werte an EXECUTE zu übergeben:

[...]
FOR rec_pooldata IN SELECT psr_no, psr_pro_no FROM public.tbl_pooldata WHERE psr_pro_no = int_project_no ORDER BY psr_no
LOOP

vch_insert_t_subset ='INSERT INTO t_subset [...];

vch_rec_pooldata_psr_no := CAST(rec_pooldata.psr_no AS varchar); 

vch_insert_t_subset     := vch_insert_t_subset || vch_rec_pooldata_psr_no; 

EXECUTE vch_insert_t_subset;

vch_update_pooldata  = 'UPDATE public.tbl_pooldata SET psr_anzahl = (SELECT COUNT(*) FROM t_subset) WHERE psr_no =';
    vch_update_pooldata := vch_update_pooldata || vch_rec_pooldata_psr_no;
    
EXECUTE vch_update_pooldata;    

[...]
TRUNCATE TABLE t_subset;
                                               
END LOOP;
[...]

Du hast Recht, es ist zu umständlich.
du hast Recht, prinzipiell ist das Verhalten bescheuert.

Mit dem unzureichenden exception-handling hast Du übrigens auch voellig Recht, das reicht noch nicht für ein ‘state-of-the-art’ dbms.

nix kosten? Die Datenbank, ja, und was kostet der zusätzliche Zeitaufwand für die Restritionen? Man muss das von verschiedenen Seiten betrachten.

Auch hier folge ich Dir. Andere dbms haben jedoch auch Schwachstellen. Man muss, wie immer, schauen, wofür man etwas braucht. frühere mysql versionen waren flinker, auf transaktionen, trigger, prozedurale sprachen, referentielle integrität
musste man jedoch verzichten oder in die appl. verlagern. informix 7.3 wurde sehr langsam bei subqueries. die ms-sql syntaxerweiterungen sind m.E. vielfach unlogisch, umständlich und unnötig.
zu oracle kann ich nix sagen.

so hat jedes dbms seine macken, wie jedes os halt auch. ist alles immer ein tradeoff.

gruss,
sago

Hallo sago,

Du hast Recht mit Deinen Zitaten aus der DOKU. Das ist eine globale Aussage und die stimmt auch. Der Rest steht im Kleingedruckten. Das ist wie mit einem Autoverkäufer, der Dir einen Sportwagen mit Sitzgelegenheiten für 4 Personen verkauft. Er hat auch damit die Wahrheit gesagt. Das auf den Rücksitzen nur Platz für Kleinkinder ist, musst Du selbst herausfinden.

Aus meiner Erfahrung kann ich Dir sagen, der Befehl EXECUTE ‘…’ funktioniert immer wenn in … reine SQL-Befehle stehen. Er funktioniert nicht, mit dem SELECT INTO … (dieses ist Dokumentiert) und er funktioniert auch nicht, wenn in … Anweisungen stehen, die einen Bezug auf eine vorher definierte RECORD-Variable haben.


Hier mal ein Beispiel, wo und wie ich EXECUTE eingesetzt habe

CREATE OR REPLACE FUNCTION last_value (_table_ TEXT, _column_ TEXT, _value_ TEXT)
RETURNS RECORD

AS $BODY$

DECLARE

  rec                   RECORD;


  BEGIN

    FOR rec IN EXECUTE 'SELECT          *
                        FROM            ' || _table_ || '
                        WHERE           ' || _column_ || ' =    (
                                                                SELECT          max (' || _column_ || ')
                                                                FROM            ' || _table_ || '
                                                                WHERE           ' || _column_ || ' <= ' || _value_ || '
                                                                )
                       '
    LOOP
      RETURN rec;
    END LOOP;

  END;

$BODY$

LANGUAGE plpgsql;

COMMENT ON FUNCTION last_value (_table_ TEXT, _column_ TEXT, _value_ TEXT) IS 'Liefert den Record zurück dem der Wert "_value_" am nächsten entspricht ';

PS: Kopier Dir den Kode heraus, ich habe TAB durch BLANK ersetzt.



mfg

fristo