Fehler: cannot EXECUTE a null querystring

Hallo,

ich bekomme bei Folgender Funktion diese Fehlermeldung:

FEHLER:  cannot EXECUTE a null querystring
CONTEXT:  PL/pgSQL function "kblatt_rekurs" line 86 at execute statement
SQL-Anweisung »SELECT  dueker.kblatt_rekurs( $1 ,  $2 ,  $1 ,  $2 ,  $3 ,  $4 ,  $5 ,  $6 )«
PL/pgSQL function "kblatt_pos" line 62 at perf

Das hier ist die Funktion:

/***********************************************************************
 * Funktion zur Aufbereitung der KBlatt Tabellenteile.
 * Übertrag in eine temporäre Tabelle für die Zuordnung zum
 * übergeordneten Artikel und Vergabe der jeweiligen Stufe.
 ***********************************************************************/

DECLARE
  stufe integer;           --Jeweilige Stufe des Artikels
  haupt_sn varchar(40);    --Satznummer KBlatt des Oberartikels (fertiges Teil)
  haupt_art varchar(40);   --Satznummer des Oberartikels (fertiges Teil)
  zeilen integer;          --Anzahl der Zeilen bei SELECT Statement (wird für IF-Konstrukt benötigt)
  stufe0 RECORD;           --RECORD zum durch-iterieren der Datensätze
  mandant varchar;         --Mandantennr
  xkalk_art_id varchar;     --ID des fertigen Artikels (mandant und sn)
  xkalk_art varchar;        --Artikelnr des fertigen Artikels
  stmt varchar;             --SQL Statement

BEGIN
  stufe:= 0;
  mandant:= mandantennr;

  --Temporäre Tabelle anlegen
  stmt:= 'CREATE TEMPORARY TABLE tmp_kblatt_pos (';
  stmt:= stmt || 'tkalk_art_id varchar(35), ';
  stmt:= stmt || 'tkalk_art varchar(30), ';
  stmt:= stmt || 'tmandantennr varchar(5), ';
  stmt:= stmt || 'telement_id varchar(35), ';
  stmt:= stmt || 'tstufe varchar(3), ';
  stmt:= stmt || 'telementtyp varchar(3), ';
  stmt:= stmt || 'tkalk_menge numeric(16,6), ';
  stmt:= stmt || 'tmengeneinheit varchar(6), ';
  stmt:= stmt || 'tvorgabezeit numeric(16,6), ';
  stmt:= stmt || 'tbfrist numeric(9,6), ';
  stmt:= stmt || 'tgewicht numeric(16,6), ';
  stmt:= stmt || 'tkblatt_id varchar(30), ';
  stmt:= stmt || 'tfehler varchar(4), ';
  stmt:= stmt || 'tkalk_typ varchar(35), ';
  stmt:= stmt || 'tegeinzk numeric(18,6), ';
  stmt:= stmt || 'teggemeink numeric(18,6), ';
  stmt:= stmt || 'teggesamk numeric(18,6), ';
  stmt:= stmt || 'tkostentyp varchar(13), ';
  stmt:= stmt || 'telem_sn_id varchar(50), ';
  stmt:= stmt || 'PRIMARY KEY (telem_sn_id, tkostentyp, tkalk_art)';
  stmt:= stmt || ') without oids;';

  EXECUTE stmt;

  FOR stufe0 IN SELECT _sn, _art FROM duk.kblatt_k LOOP
      --Erste Schleife Stufe 0 (komplette kblatt_k Tabelle)
      haupt_sn:= stufe0._sn;
      haupt_art:= stufe0._art;

      xkalk_art_id := mandant || '_' || haupt_art;
      SELECT artikel_k_id INTO xkalk_art FROM dueker.dim_artikel_kopf WHERE sn_kopf_id = xkalk_art_id;

      SELECT COUNT(*) INTO zeilen FROM duk.kblatt_pos WHERE _sn = haupt_sn AND arch = 0;

      IF zeilen <> 0 THEN
        -- Funktionsaufruf der rekursiven Funktion
       [U] PERFORM dueker.kblatt_rekurs(haupt_sn, haupt_art, haupt_sn, haupt_art, mandant, stufe, xkalk_art, xkalk_art_id);[/U]
      ELSE
        -- nichts
      END IF;

  END LOOP;

  --PERFORM dueker.kblatt_pos_fuellen();

END

Die Unterstrichene Zeile ist die in der Fehlermeldung angemeckerte Zeile.


Und die Funktion, die aus dieser Funktion ausgerufen wird:

/***********************************************************************
 * Funktion zur Aufbereitung der KBlatt Tabellenteile - rekursiver Teil.
 * Ruft sich bei finden von weiteren Unterstufen wieder selbst auf.
 * Übertrag in die Tabellen im Mandant dueker und vergabe, Zuordnung zum
 *  Übergeordneten Artikel und Vergabe der jeweiligen Stufe.
 ***********************************************************************/


DECLARE
  haupt_sn varchar;         --Satznummer übergeordnetes KBlatt_kopf (fertiger Artikel)
  haupt_art varchar;        --Satznummer übergeordneter Artikel (fertiger Artikel)
  artikel_sn varchar;       --Satznummer Artikel in der Fertigungsliste
  stufen_sn varchar;        --Satznummer KBlatt_kopf von untergerodneten ARtikeln aus der FL
  xtemp varchar;            --Temporäre Variable
  stufe integer;            --Aktuelle Stufe des Teil im Bezug auf Oberartikel (fertiges Teil)
  rec record;               --RECORD zum durchiterieren der Datensätze
  ergebnis integer;         --Ergebnis der SELECT Abfrage (für IF-Konstrukt)
  stmt1 varchar;            --SQL Statement
  stmt varchar;             --SQL Statement
  mandant varchar;          --Mandantennr
  xkalk_art_id varchar;     --ID des fertigen Artikels (mandant und sn)
  xkalk_art varchar;        --Artikelnr des fertigen Artikels


BEGIN
  haupt_sn := hauptsn;
  haupt_art := hauptart;
  artikel_sn := artikelsn;
  stufen_sn := stufensn;
  mandant := mandantennr;
  stufe := pstufe + 1;
  xkalk_art_id := kalkartid;
  xkalk_art := kalkart;

  --zu füllende Felder in der Temp-Tabelle auswählen
  stmt1:= 'INSERT INTO tmp_kblatt_pos (';
  stmt1:= stmt1 ||'tkalk_art_id,';
  stmt1:= stmt1 ||'tkalk_art,';
  stmt1:= stmt1 ||'tmandantennr,';
  stmt1:= stmt1 ||'telement_id,';
  stmt1:= stmt1 ||'tstufe,';
  stmt1:= stmt1 ||'telementtyp,';
  stmt1:= stmt1 ||'tkalk_menge,';
  stmt1:= stmt1 ||'tmengeneinheit,';
  stmt1:= stmt1 ||'tvorgabezeit,';
  stmt1:= stmt1 ||'tbfrist,';
  stmt1:= stmt1 ||'tgewicht,';
  stmt1:= stmt1 ||'tkblatt_id,';
  stmt1:= stmt1 ||'tfehler,';
  stmt1:= stmt1 ||'tkalk_typ,';
  stmt1:= stmt1 ||'tegeinzk,';
  stmt1:= stmt1 ||'teggemeink,';
  stmt1:= stmt1 ||'teggesamk,';
  stmt1:= stmt1 ||'tkostentyp, ';
  stmt1:= stmt1 ||'telem_sn_id ';
  stmt1:= stmt1 ||') VALUES(';

  --Felder in der Temp-Tabelle füllen
  FOR rec IN SELECT * FROM duk.kblatt_pos WHERE _sn = stufen_sn AND arch = 0 LOOP
    stmt:= stmt1 || '\'' || xkalk_art_id || '\', ';              --tkalk_art_id
    stmt:= stmt || '\'' || xkalk_art || '\', ';                  --tkalk_art
    stmt:= stmt || '\'' || mandant || '\', ';                    --tmandantennr
    xtemp:= cast (mandant ||'_'||rec._elem as varchar(35));
    stmt:= stmt || '\'' || xtemp || '\', ';                      --telement_id
    stmt:= stmt || '\'' || stufe || '\', ';                      --tstufe
    stmt:= stmt || '\'' || rec._elart || '\', ';                 --telementtyp
    stmt:= stmt || '\'' || rec._ekmge || '\', ';                 --tkalk_menge
    stmt:= stmt || '\'' || rec._ele || '\', ';                    --tmengeneinheit
    stmt:= stmt || '\'' || rec._evzeit || '\', ';                --tvorgabezeit
    stmt:= stmt || '\'' || rec._ebfrist || '\', ';               --tbfrist
    stmt:= stmt || '\'' || rec._egewicht || '\', ';              --tgewicht
    xtemp:= cast(mandant ||'_'|| rec._ekblatt as varchar(30));
    stmt:= stmt || '\'' || xtemp || '\', ';                      --tkblatt_id
    stmt:= stmt || '\'' || rec._efehler || '\', ';               --tfehler
    stmt:= stmt || '\'' || rec._typ || '\', ';                   --tkalk_typ
    stmt:= stmt || '\'' || rec._egeinzk || '\', ';               --tegeinzk
    stmt:= stmt || '\'' || rec._eggemeink || '\', ';             --teggemeink
    stmt:= stmt || '\'' || rec._eggesamk || '\', ';              --teggesamk
    stmt:= stmt || '\'' || rec._ekart || '\', ';                   --tkostentyp
    xtemp:= rec._sn || '_' || rec._elem;
    stmt:= stmt || '\'' || xtemp || '\'';                      --telem_sn_id
    stmt:= stmt || ');';

    EXECUTE stmt;

  END LOOP;

END

Vielleicht kann mir ja jemand helfen, ich weiss nicht wo ich den Fehler suchen muss…

Ohne jetzt mir alles durchzulesen: du bastelst ein dynamisches Query zusammen. Wenn einer der beteiligten String-Komponenten NULL ist, dann ist ‘foo’ || NULL || ‘bar’ auch NULL.

Also, pfrieml Dein Query-String mit coalesce(var, ‘’) oder etwas vergleichbarem zusammen.

Andreas

[/quote]

Danke. Wie funktioniert das damit?

edit: glaube hab gerade was dazu gefunden hier im forum.



test=# create table person (id serial primary key, vorname text, nachname text);
NOTICE:  CREATE TABLE will create implicit sequence "person_id_seq" for serial column "person.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "person_pkey" for table "person"
CREATE TABLE
test=*# insert into person (vorname, nachname) values (NULL, 'eins');
INSERT 0 1
test=*# insert into person (vorname, nachname) values ('zwei', NULL);
INSERT 0 1
test=*# insert into person (vorname, nachname) values ('drei', 'drei');
INSERT 0 1
test=*# select id, vorname || ' ' || nachname from person;
 id | ?column?
----+-----------
  1 |
  2 |
  3 | drei drei
(3 rows)

test=*# select id, coalesce(vorname,'') || ' ' || coalesce(nachname,'') from person;
 id | ?column?
----+-----------
  1 |  eins
  2 | zwei
  3 | drei drei
(3 rows)

Andreas

Ansonsten macht es für das Debuggen auch Sinn, sich die Queries vor dem Ausführen mal Auszugeben. Ein RAISE NOTICE ist da sehr hilfreich.

Noch eine Frage dazu, was passiert wenn in einer Select-Abfrage keine Zeilen gefunden werden und ich ein LOOP hab, könnte das das Problem sein?

Denn bei dem zusammengebastelten INSERT-Statement arbeite ich ja mit Variablen, die eigentlich prinzipiell auch “null” sein können, also sollten sie zumindest.
Oder darf hier gar kein wert “null” sein beim zusammensetzen ?

Noch einmal: wenn Du aus 3 Variablen einen String zusammensetzt, und eine Variable ist NULL, dann ist der resultierende String auch NULL. Klingt lustig, ist aber so. Du kannst es gern testen: select ‘foo’ || NULL || ‘bar’; Und der Hinweis von ads, mal via RAISE das auszugeben, darf auch befolgt werden. Und der Hinweis auf coalesce kam wohl auch schon, und Du darfst auch mal select ‘foo’ || coalesce(NULL, ‘da ist nix’) || ‘bar’; testen.

Andreas

Danke, werd das mal ausprobieren.
Tut mir ja leid für die blöde Fragerei, aber ich wurde hier mit PSQL ins kalte Wasser geworfen und hatte leider noch nicht damit gearbeitet und auch kaum Unterlagen dazu. Bin gerade dabei mich da schritt für schritt einzuarbeiten und dabei meine Datenaufbereitung zum Laufen bekommen…

Also Danke für euere geduldige Hilfe.

Kein Problem. Die Rechnung kommt erst später. Wenn es geht.

Andreas

Kann ich COALESCE irgendwie für * benutzen oder muss jedes Feld explizit mit COALESCE(blablub) angeben ?

edit: ausserdem bekomme ich bei der Verwendung von RAISE den folgenden Fehhler:
FEHLER: too many parameters specified for RAISE

Statement zu lang ?

Sorry, nocheinmal eine Verständnisfrage zu COALESCE.
Wenn ich folgendes ausprobiere erhalte ich als Ergebnis NULL: SELECT ‘123’ || coalesce(null) || ‘456’;
Wie ja schon gesagt, wenn ein Teilstring NULL ist ist der ganze Strin NULL.
Die Variablen, die ich in dem Zusammengebastelten Feld verwende werden vorher ja ja mit einem SELECT Statement gefüllt (bei dem ich dann COALESCE verwenden würde). Aber hilft mit COALESCE hier überhaupt, wenn in den Datensätzen manche Felder NULL sind, denn COALESCE(NULL) ist ja auch NULL - also müsste ich jedes Feld auf NULL prüfen eigentlich, oder ?

Sehr verwirrdend das alles…

Nein, ja.

edit: ausserdem bekomme ich bei der Verwendung von RAISE den folgenden Fehhler:
FEHLER: too many parameters specified for RAISE

Statement zu lang ?

Nein.

test=> create or replace function raise_test() returns int as $$ begin raise notice '% %', 'a', 'b'; return 1; end; $$language plpgsq                            l;
CREATE FUNCTION
test=*> select raise_test();
NOTICE:  a b
 raise_test
------------
          1
(1 row)

test=*> rollback;
ROLLBACK
test=> create or replace function raise_test() returns int as $$ begin raise notice '% %', 'a', 'b', 'c'; return 1; end; $$language plpgsql;
CREATE FUNCTION
test=*> select raise_test();
ERROR:  too many parameters specified for RAISE
CONTEXT:  PL/pgSQL function "raise_test" line 1 at raise

Siehst Du, was flasch ist?

Andreas

COALESCE prüft, ob der erste Parameter NULL ist. Ist dem so, liefert es den zweiten Parameter. Ist dann halt dumm, wenn der auch NULL ist.

Andreas

Ja, ein Parameter zu viel, ich hatte das % ZEichen vergessen.


COALESCE prüft, ob der erste Parameter NULL ist. Ist dem so, liefert es den zweiten Parameter. Ist dann halt dumm, wenn der auch NULL ist.

Aha, eigentlich soll ja eben nur der eine Paramater geprüft werden und auch genau dieser weitergegeben werden. d.h. wenn der in der Unsprungstabelle “null” ist soll er auch in er Zieltabelle “null” sein.

Ja, ein Parameter zu viel, ich hatte das % ZEichen vergessen.


COALESCE prüft, ob der erste Parameter NULL ist. Ist dem so, liefert es den zweiten Parameter. Ist dann halt dumm, wenn der auch NULL ist.

Aha, eigentlich soll ja eben nur der eine Paramater geprüft werden und auch genau dieser weitergegeben werden. d.h. wenn der in der Unsprungstabelle “null” ist soll er auch in er Zieltabelle “null” sein.
Irgendwie versteh ich’s glaube ich immer noch nicht.
edit: Mit COALESCE könnte ich ihn dann quasi auf z.B. ‘’ setzen oder jeden möglichen anderen Wert sofern der erste null ist, wenn ich richtig verstehe?

Also nochmal mein Anliegen im Ganzen.
Ich mache eien Aufbereitung für Kalkulationsdatensätze. Die werden in die Datenbank importiert aus dem ERP System und sind in Kopf und Tabelleteil getrennt, je eine Tabelle. Diese Daten möchte ich nun in zwei neue Tabellen übernehmen und dabei noch die Unterartikel zu dem zugehörigen fertigen Artikel zuordnen sowie eine Stufennummer für die einzelnen Bauteile vergeben - deswegen die Aufbereitung.
Dabei gehe ich praktisch Datensatz für Datensatz durch, mache ein paar berechnungen und übergeben in zuerst in eine Temporäre Tabelle und dann in die neue Tabelle. Wenn jetzt ein Feld in der Ursprungstabelle NULL ist soll es später auch wieder NULL sein…


Ich hoffe das ist halbwegs nachvollziehbar.

Nochmals danke für die Bemühungen.

Du musst jedes Feld explizit angeben … ein alternativer Weg wäre es, die Felder sofort als NOT NULL zu definieren und einen DEFAULT ‘’ vorzugeben, das erspart das COALESCE. Da weiss ich aber nicht, ob das in dein Datenmodell passt oder ob du den NULL Wert ev. benötigst.

edit: ausserdem bekomme ich bei der Verwendung von RAISE den folgenden Fehhler:
FEHLER: too many parameters specified for RAISE

Statement zu lang ?

Zu Lang? Nein. Aber woher sollen wir den Fehler wissen, wenn wir nicht sehen, was genau du probierst? seufz

XyProblem - Greg’s Wiki

Also über den Weg mit COALESCE und einem Alternativwert von ‘0’ bzw. ‘’ läuft das Ganze jetzt durch.
Allerdings wie gesagt wären mir die null-werte lieber. Gibt es einen Weg diese zu übernehmen?

Wenn Du in einem INSERT ein NULL-Wert eingeben willst, dann muß dort auch NULL stehen und nicht einfach “nichts”.

test=*> select i1 from quelle;
 i1
----
  1

  3
(3 rows)

test=*> select coalesce (i1::text, 'NULL') from quelle;
 coalesce
----------
 1
 NULL
 3
(3 rows)

Und wenn Du daraus INSERT-Befehle generieren willst:

test=> select 'INSERT INTO ziel (ziel_spalte) VALUES (' || coalesce (i1::text, 'NULL') || ');' from quelle;
                   ?column?
-----------------------------------------------
 INSERT INTO ziel (ziel_spalte) VALUES (1);
 INSERT INTO ziel (ziel_spalte) VALUES (NULL);
 INSERT INTO ziel (ziel_spalte) VALUES (3);

Andreas

Hallo,

erstmal, meine Prozedur läuft jetzt soweit - danke!
Ein kleines Problemchen hab ich noch. Ich habe das Problem dass bei den INSERT-Befehlen duplizierte Schlüssel vorkommen.

Kann ich die Fehlermeldung “FEHLER: duplizierter Schlüssel verletzt Unique-Constraint »fact_kblatt_pos_pkey«” irgendwie aufgreifen und bewirken, dass dieser Datensatz einfach verworfen wird und die schleife weiter druchlaufen wird, anstatt dass die Prozedur mit eben diesem Fehler abbricht ?

PostgreSQL: Documentation: Manuals: PostgreSQL 8.2: Control Structures
Example 37-1. Exceptions with UPDATE/INSERT

Andreas