-
Das Problem ist halt manchmal der Autocast.
Wenn die Feldtypen nicht stimmen, versucht SQL eine Typanpassung.
Bei dynamischem SQL klappt dies meistens, beim statischen SQL (wie in der Prozedur) kann es da scon Probleme geben. Wobei diese hier eher C-spezifisch sind.
Die Frage ist hier: Wie rufst du die Prozedur denn auf "select * from MyProc(?, ?, ...)"?
Wichtig ist hier ggf. die korrekte Parameterdefinition im Command-Objekt.
Im Normalfall solltest du deinen CommandText mit Parametermarkern "?" definieren und dann Parameters.Refresh (o.ä.) aufrufen um die korrekte Definition festzustellen.
Benannte Parameter werden ggf. dabei nicht unterstützt.
Arbeitest du ohne Parametermarker so ist die korrekte Übergabe der Werte zwingend Voraussetzung.
Bei Zahlen sind genau so viele Ziffern erforderlich wie erwartet, ebenso auch das Komma bzw. der Punkt.
Z.B. insert into MyTable (F1, F2) values(123456789,12 , 12345,12 )
Dabei sind dann ebenso Vornullen zu formatieren.
SQL legt dann auf dem Server ein cached Statement mit genau der Parameter-Definition an, die der 1. Aufruf vorgibt. Wenn du dann beim nächsten Aufruf andere Parameter übergibst, kommt es zu Konvertierungsproblemen.
Deshalb bei solchen Sachen immer mit Parametermarkern arbeiten.
Casts sind da auch nicht hilfreich, da Konstanten vom Optimizer intern entfernt und wiederum selber als Parametermarker definiert werden und wiederum der Typ an Hand der Konstante bestimmt wird.
-
 Zitat von Fuerchau
Das Problem ist halt manchmal der Autocast.
Wenn die Feldtypen nicht stimmen, versucht SQL eine Typanpassung.
...
Die Frage ist hier: Wie rufst du die Prozedur denn auf "select * from MyProc(?, ?, ...)"?
Über SQL Prozedur ausführen im Nachgang an die Erstellungsroutine der SQL Prozedur habe ich eine "Test"-Rubrik wo ich im o.g. Fall z.B. 2 x mal den gleichen Command absetze:
Code:
call hgwobj.vag13_insert(5, 23, 22.233, 'WRKSTN', 'SQL', 'SQL Prozedur ausführen');
call hgwobj.vag13_insert(5, 23, 22.233, 'WRKSTN', 'SQL', 'SQL Prozedur ausführen');
Der erste klappt, der zweite nicht.
Wenn ich das FINAL Table in der SQL-Prozedur weglasse u. stattdessen "old-school" über IDENTITY_VAL_LOCAL() arbeite um mir die ID des Inserts zu holen und im Anschluss den Cursor über den/die Sätze zurückgebe klappt es ja ohne Probleme!
Hier der geänderte Teil der SQL Prozedur:
Code:
BEGIN
DECLARE pId INTEGER DEFAULT 0;
DECLARE C1 CURSOR WITH RETURN TO CALLER FOR
SELECT * FROM vVag13P01 where id = pId;
INSERT INTO vVag13P01
(
RabattId, Menge, RabattWert
,WRKSTNNAME, CLIENTPROGRAMID, CLIENTAPPLNAME
)
VALUES
(
pRabattId, pMenge, pRabattWert
,pWRKSTNNAME, pCLIENTPROGRAMID, pCLIENTAPPLNAME
);
SET pId = IDENTITY_VAL_LOCAL();
IF (pId > 0 ) THEN
-- Cursor öffnen u. zurückgeben
OPEN C1;
SET RESULT SETS WITH RETURN TO CALLER CURSOR C1;
END IF;
END;
Auch wenn ich es in einem BEGIN/END-Block zusammenfasse um saubere Variablen vom gleichen Typ zu haben, kommt der gleiche Fehler:
Code:
BEGIN
DECLARE pRabattId INTEGER;
DECLARE pMenge decimal(11,3);
DECLARE pRabattWert decimal (5,2);
-- Systemfelder
DECLARE pWRKSTNNAME VARCHAR(50);
DECLARE pCLIENTPROGRAM VARCHAR(50);
DECLARE pCLIENTAPPLNAME VARCHAR(50);
SET pRabattId = 5;
SET pMenge = 00000001.000;
SET pRabattWert = 000.33;
SET pWRKSTNNAME = 'WRKSTN';
SET pCLIENTPROGRAM = 'CPGMID';
SET pCLIENTAPPLNAME = 'APPLNAME';
-- call hgwobj.vag13_insert(5, 1, 2, 'WRKSTN', 'SQL', 'SQL Prozedur ausführen');
call hgwobj.vag13_insert(pRabattId, pMenge, pRabattWert, pWRKSTNNAME, pCLIENTPROGRAM, pCLIENTAPPLNAME);
SET pMenge = 00000002.200;
SET pRabattWert = 001.33;
call hgwobj.vag13_insert(pRabattId, pMenge, pRabattWert, pWRKSTNNAME, pCLIENTPROGRAM, pCLIENTAPPLNAME);
rollback;
END;
Fazit für mich ist erstmal, dass ich das FINAL Table hier lieber sein lasse u. mir den Cursor selbst basierend auf der generierten ID zurückliefern lasse. Das ist vermutlich auch für Tabellen ohne Autoinkrementspalte der einzige Weg, die Änderungen der Datenbank abzufragen.
-
Oder mal eine Fehlermeldung an IBM abgeben. Auch die IBM soll ja nicht ganz fehlerfrei arbeiten.
-
... ich finde irgendwie kein close auf den Cursor. Declare cursor ist eine rein deklarative Compiler Anweisung, beim open wird dann ein impliziter prepare mit gemacht. Das müsste eigentlich noch ein warning (cursor state not valid) beim zweiten Aufruf bringen.
Darüber hinaus gibt es noch ein paar Dinge, die ich nicht verstehe:
- warum erfolgt die Rückgabe per ResultSet?
- warum erfolgt die Rückgabe nicht über in/out parameter? (alle Felder hin, die generierten Werte kommen als out verändert zurück)
- weshalb der cast auf den Timestamp?
- warum heißt der changed Timestamp Version (als Version taugt der nicht, das kann schief gehen!)?
- generell sollte man condition handler für error vorsehen, sonst düst man unversehens raus.
Dieter Bender
-
Der Timestamp-Cast ist wohl dem ODBC-Zugriff (C#) geschuldet, da dieser ggf. damit nicht zurechtkommt.
Für den CLose ist doch eigentlich der Aufrufer zuständig, wobei dieser ja vom Cursor-Namen nichts kennt.
Aber ohne Final Table scheints ja zu funktionieren.
-
Hallo Herr Bender,
Hallo Herr Fürchau,
danke für die Antwort. Aktuell habe ich ein Ticket bei IBM zu dem Thema laufen. Mal sehen was zu dem Thema mit dem Final Table herauskommt.
 Zitat von BenderD
1. ... ich finde irgendwie kein close auf den Cursor. Declare cursor ist eine rein deklarative Compiler Anweisung, beim open wird dann ein impliziter prepare mit gemacht. Das müsste eigentlich noch ein warning (cursor state not valid) beim zweiten Aufruf bringen.
Darüber hinaus gibt es noch ein paar Dinge, die ich nicht verstehe:
- 2. warum erfolgt die Rückgabe per ResultSet?
- 3. warum erfolgt die Rückgabe nicht über in/out parameter? (alle Felder hin, die generierten Werte kommen als out verändert zurück)
- 4. weshalb der cast auf den Timestamp?
- 5. warum heißt der changed Timestamp Version (als Version taugt der nicht, das kann schief gehen!)?
- 6. generell sollte man condition handler für error vorsehen, sonst düst man unversehens raus.
Dieter Bender
- Das Resultset soll an die aufrufende Instanz (C# Command-Objekt bzw. ILERPG) zurückgeliefert werden können. Ich habe es eben nochmals gestestet, wenn ich den CLOSE Aufrufe kommt kein Resultset zurück. Ein Warning habe ich keines in den Nachrichten bzw. Joblog gesehen.
- Eine Rückgabe per Result ist meiner Meinung nach flexibler, d.h. ich liefere einen Satz zurück u. nicht mittels Versorgung von 50 INOUT Parametern. Das wäre im C# der Tot eine solche Parameterlatte in einer Schleife in eine DataTable zu pumpen bzw. bei Verwendung von typisierten DataSets (In-Memory-Datenbank mit Relation) arbeitet man mit TableAdaptern - diese benötigen ein Resultset u. können keine Satzweise Verarbeitung.
- siehe 2.
- Wie Herr Fürchau angemerkt hat, ist leider der ODBC Treiber nicht in der Lage TIMESTAMP(6) darzustellen, sondern schafft maximal TIMESTAMP(0) - zumindest in der Standardeinstellung. Durch den CAST auf einen String erzeuge ich quasi eine "Pseudo-GUID" -mit Gültigkeit für den einen Datensatz - bitte nicht falsch Verstehen, es ist kein Primary Key!
- Bislang keine Probleme gehabt - zählt "Version" zu den reservierten Schlüsselwörtern? Wäre RowVersion besser?
- Thema Exceptionhandling ist hier an dieser Stelle nicht gewollt - die Aufrufende Instanz der Logikschicht (AS400 über den SQLCode u. C# bekommt bei Fehler eine OdbcException) soll je nach Fehler reagieren.
-
Statt ODBC-Treiber sollte man den OLEDB-Treiber (IBMDASQL) verwenden.
Dann braucht man auch keine ODBC-Registrierung.
Zur Vereinfachung der Konfiguration kann man eine "xxx.UDL"-Datei erstellen. Diese lässt sich per Doppelklick auch einfach konfigurieren.
In einer OLEDB-Connection gebe ich die Konfiguration einfach so an:
ConnectionString = "File Name=c:\test.udl"
-
 Zitat von Gutmann
Hallo Herr Bender,
Hallo Herr Fürchau,
danke für die Antwort. Aktuell habe ich ein Ticket bei IBM zu dem Thema laufen. Mal sehen was zu dem Thema mit dem Final Table herauskommt.
- Das Resultset soll an die aufrufende Instanz (C# Command-Objekt bzw. ILERPG) zurückgeliefert werden können. Ich habe es eben nochmals gestestet, wenn ich den CLOSE Aufrufe kommt kein Resultset zurück. Ein Warning habe ich keines in den Nachrichten bzw. Joblog gesehen.
- Eine Rückgabe per Result ist meiner Meinung nach flexibler, d.h. ich liefere einen Satz zurück u. nicht mittels Versorgung von 50 INOUT Parametern. Das wäre im C# der Tot eine solche Parameterlatte in einer Schleife in eine DataTable zu pumpen bzw. bei Verwendung von typisierten DataSets (In-Memory-Datenbank mit Relation) arbeitet man mit TableAdaptern - diese benötigen ein Resultset u. können keine Satzweise Verarbeitung.
- siehe 2.
- Wie Herr Fürchau angemerkt hat, ist leider der ODBC Treiber nicht in der Lage TIMESTAMP(6) darzustellen, sondern schafft maximal TIMESTAMP(0) - zumindest in der Standardeinstellung. Durch den CAST auf einen String erzeuge ich quasi eine "Pseudo-GUID" -mit Gültigkeit für den einen Datensatz - bitte nicht falsch Verstehen, es ist kein Primary Key!
- Bislang keine Probleme gehabt - zählt "Version" zu den reservierten Schlüsselwörtern? Wäre RowVersion besser?
- Thema Exceptionhandling ist hier an dieser Stelle nicht gewollt - die Aufrufende Instanz der Logikschicht (AS400 über den SQLCode u. C# bekommt bei Fehler eine OdbcException) soll je nach Fehler reagieren.
ad 1.: das ResultSet muss bei der Rückgabe geöffnet sein. Die Frage bezog sich darauf, dass es vor dem erneuten open closed sein muss (also am Anfang close!)
ad 2/3.: Das ist halt die Crux mit den stored Procedures, da gibt es im handling der verschiedenen SQL Dialekte Unterschiede, die halt zu Randproblemen führen können.
ad 4.: der insert in die View mit dem cast ist schon ein wenig schwindelig...
ad 5.: VERSION ist in der Tat reserviertes Wort (könnte man aber rausmaskieren mit :"VERSION", was ich meinte war, dass Timestamps nicht eindeutig sind.
ad 6.: Ohne Continue handler kriegt man innerhalb der Procedure nichts mehr mit und düst direkt raus. Das ist gerade im verteilten Umfeld nicht so toll, da sollte man zumindest das ein oder andere noch protokolieren...
D*B
-
IBM hat ein PTF SI64636 erstellt damit der Fehler mit dem Final-TABLE Statement behoben ist. Testen werden wir das ganze allerdings erst zum Jahreswechsel.
Similar Threads
-
By svit in forum NEWSboard Programmierung
Antworten: 2
Letzter Beitrag: 03-03-16, 11:11
-
By JotSo in forum IBM i Hauptforum
Antworten: 9
Letzter Beitrag: 22-12-15, 12:08
-
By Etherion in forum NEWSboard Programmierung
Antworten: 6
Letzter Beitrag: 30-09-14, 13:36
-
By mk in forum NEWSboard Programmierung
Antworten: 3
Letzter Beitrag: 22-09-14, 06:58
-
By JMH in forum IBM i Hauptforum
Antworten: 7
Letzter Beitrag: 15-01-14, 14:53
Tags for this Thread
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- You may not post attachments
- You may not edit your posts
-
Foren-Regeln
|
Erweiterte Foren Suche
Google Foren Suche
Forum & Artikel Update eMail
AS/400 / IBM i
Server Expert Gruppen
Unternehmens IT
|
Kategorien online Artikel
- Big Data, Analytics, BI, MIS
- Cloud, Social Media, Devices
- DMS, Archivierung, Druck
- ERP + Add-ons, Business Software
- Hochverfügbarkeit
- Human Resources, Personal
- IBM Announcements
- IT-Karikaturen
- Leitartikel
- Load`n`go
- Messen, Veranstaltungen
- NEWSolutions Dossiers
- Programmierung
- Security
- Software Development + Change Mgmt.
- Solutions & Provider
- Speicher – Storage
- Strategische Berichte
- Systemmanagement
- Tools, Hot-Tips
Auf dem Laufenden bleiben
|
Bookmarks