Entwickler-Ecke

Datenbanken - Rückgabewerte von Stored Procedures


UliTs - Do 13.02.20 17:32
Titel: Rückgabewerte von Stored Procedures
Ausgehend hiervon: Script in MySql schreiben [https://entwickler-ecke.de/topic_Script+in+MySql+schreiben_117861.html]
habe ich inzwischen mit Erfolg diverse Stored Procedures geschrieben.

Dabei gab es bisher aber keine Rückgabewerte. Jetzt möchte ich mehrere Rückgabewerte realisieren und abfragen.
Wie macht man das?


Moderiert von user profile iconTh69: URL-Titel hinzugefügt.


OlafSt - Do 13.02.20 17:45

Das ist sehr einfach. Anstelle DataSet.ExecSQL rufst du die Stored Procedure mit DataSet.Open; auf.

Anschließend kannst du mit DataSet.Fields[n].AsXXX die Rückgabewerte der StoredProc abholen. FieldByName funktioniert nicht, da die Return Values ja keine Feldnamen haben.

DataSet.Close; nicht vergessen ;)


UliTs - Do 13.02.20 22:25

Hallo OlafSt,

schon einmal vielen Dank! Das hört sich gut an. Ich habe es bisher unter anderem mit FDQuery versucht, bin aber damit gescheitert.
Heute Nacht habe ich Zeit und versuche es...

Viele Grüße

Uli


UliTs - Fr 14.02.20 14:01

Bin frustriert.
Komme kein Stück weiter ...

Wenn ich eine einfache Stored Procedure nehme mit einem Integer-Eingabe-Parameter und als Ergebnis soll der Eingabewert +1 und ein weiteres Zeichen-Feld mit dem Wert 'TestProcedure' herauskommen, wie kann ich dann die Rückgabewerte abfragen?

also z.B.

Quelltext
1:
2:
3:
4:
5:
stored procedure TestUli( in Ziffer int, out Ziffer2 int, out Bemerkung text );
begin
  set Ziffer2 = Ziffer+1;
  set Bemerkung = 'TestProcedure';
end

und dann?

Moderiert von user profile iconTh69: Code-Tags hinzugefügt


Th69 - Fr 14.02.20 14:35

Schau mal in Parameter für Stored Procedures verwenden [http://docwiki.embarcadero.com/RADStudio/Rio/de/Parameter_f%C3%BCr_Stored_Procedures_verwenden].


UliTs - Fr 14.02.20 16:01

Hallo Th69,

vielen Dank für den Link. Leider bin ich damit nicht weiter gekommen.
Allerdings habe ich beim Einsatz von TFDQuery folgendes festgestellt: ich kann damit mittels "call" die Stored Procedure ausführen. Als Rückgabe kommen die Werte des ersten innerhalb der Stored Procedure vorkommenden select-Befehls zurück (wie sinnfrei ...). Wenn es die Werte des letzten select-Befehls wären, würde es wunderbar funktionieren.


UliTs - Sa 15.02.20 12:23

Bei der folgenden Procedure

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure Test( in Nr int );
begin
  declare Result int;

  select Nr-5;
  set Result = (select Nr+5);
  select Nr,Result;
end

würde ich z.B. bei Aufruf von

Quelltext
1:
call Test( 100 );                    

als Rückgabe
Zitat:
100 , 105

erwarten. Es kommt aber
Zitat:
95

als Rückgabewert :-( .

Wie komme ich an das Ergebnis des letzten select-Statements?


Ralf Jansen - Sa 15.02.20 13:38

Die StoredProc Klasse wie sie auch im Link von user profile iconTh69 benutzt wird hat eine NextRecordSet [http://docwiki.embarcadero.com/RADStudio/Rio/en/Fetching_Multiple_Result_Sets] Methode um an die weiteren Datasets zu kommen


Th69 - Sa 15.02.20 15:44

@UliTs: Ist die Stored Procedure von dir? Warum benutzt du denn dort verschiedene select Anweisungen?


UliTs - So 16.02.20 23:46

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
@UliTs: Ist die Stored Procedure von dir? Warum benutzt du denn dort verschiedene select Anweisungen?

Wie geschrieben um mein Problem zu zeigen.


UliTs - Mo 17.02.20 00:18

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Die StoredProc Klasse wie sie auch im Link von user profile iconTh69 benutzt wird hat eine NextRecordSet [http://docwiki.embarcadero.com/RADStudio/Rio/en/Fetching_Multiple_Result_Sets] Methode um an die weiteren Datasets zu kommen

Ich habe darüber nachgedacht und glaube, dass dies genau mein Problem ist!
Wenn ich es richtig verstehe, kann eine Stored Procedure nicht nur eine Tabelle sondern mehrere als Ergebnis haben.
Das will ich in meinem Fall aber gar nicht. Ich rufe aber innerhalb der Stored Procedure eine andere Stored Procedure "ProtokollEinfuegen" auf, die als Ergebnis eine Tabelle mit 1 Spalte und 1 Datensatz hat:


Quelltext
1:
2:
3:
  ...
  call ProtokollEinfuegen;
  ...


Das heisst, dies wird dann auch als eine Ergebnistabelle genommen?

Normalerweise würde ich sagen, dann mache ich


Quelltext
1:
2:
3:
  ...
  set PrResult = (call ProtokollEinfuegen);
  ...

und schon sollte es funktionieren.
Aber dann bekomme ich eine Fehlermeldung und kann die Stored Procedure nicht speichern...


Th69 - Mo 17.02.20 10:20

Also enthält ProtokollEinfuegen eine select-Anweisung, welche du dann in eine eigene select-Anweisung übernehmen möchtest (damit du nur genau eine Tabelle zurückgibst)?
Das ist leider bei MySql nicht direkt möglich, s.a. How to call a stored procedure using select statement in mysql [https://stackoverflow.com/questions/32118033/how-to-call-a-stored-procedure-using-select-statement-in-mysql].
Wie aus der Antwort zu ersehen muß entweder die Procedure in eine temporäre Tabelle schreiben oder aber du machst daraus eine Funktion (UDF), welche diesen einen Datenwert zurückgibt, den du dann übernehmen kannst:

SQL-Anweisung
1:
select ProtokollEinfuegen; -- wobei du dann einen besseren Funktionsnamen nehmen solltest!                    


Edit: Sehe gerade, daß eine UDF nur in C++ erstellt werden kann (Adding a User-Defined Function [https://dev.mysql.com/doc/refman/8.0/en/adding-udf.html]) - das scheidet hier wohl aus.

PS: Nur noch mal zur Bestätigung, du verwendest MySql (denn bisher ist nur in deinem ersten Link davon zu lesen)?


Ralf Jansen - Mo 17.02.20 14:25

Zitat:
Stored Procedure "ProtokollEinfuegen" auf, die als Ergebnis eine Tabelle mit 1 Spalte und 1 Datensatz hat:


1 Spalte, 1 Zeile das klingt ziemlich Skalar. Warum ist das ein Tabelle die da zurückkommt?

Zitat:
Edit: Sehe gerade, daß eine UDF nur in C++ erstellt werden kann (Adding a User-Defined Function) - das scheidet hier wohl aus.


Eine Function (nicht UDF) die man direkt in ein anderen SQL Statement benutzen kann geht auch im üblichen MySql Syntax.


OlafSt - Mo 17.02.20 14:30

user profile iconUliTs hat folgendes geschrieben Zum zitierten Posting springen:
Bei der folgenden Procedure

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure Test( in Nr int );
begin
  declare Result int;

  select Nr-5;
  set Result = (select Nr+5);
  select Nr,Result;
end

würde ich z.B. bei Aufruf von

Quelltext
1:
call Test( 100 );                    

als Rückgabe
Zitat:
100 , 105

erwarten. Es kommt aber
Zitat:
95

als Rückgabewert :-( .

Wie komme ich an das Ergebnis des letzten select-Statements?


FDQuery.Fields[0].AsInteger sollte die 95 ausgeben.
FDQuery.Fields[1].AsInteger sollte die 105 ausgeben.

Und so weiter. Ich dachte, die Array-Eigenschaft wäre schon Hinweis genug ;)

Moderiert von user profile iconTh69: Delphi-Tags hinzugefügt


Th69 - Mo 17.02.20 15:52

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Eine Function (nicht UDF) die man direkt in ein anderen SQL Statement benutzen kann geht auch im üblichen MySql Syntax.

Oh, danke für den Hinweis - ich hatte mich schon gewundert, daß es nur über UDF funktionieren sollte. Bin beim Suchen nur auf die UDF-Seiten gestoßen, jetzt aber habe ich auch mittels Suche nach ""mysql create function" CREATE PROCEDURE and CREATE FUNCTION Statements [https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html] gefunden. Und damit ist es ja dann noch einfacher, wenn die Funktion einfach den Datensatzwert direkt zurückgibt (mittels RETURN).


UliTs - Mo 17.02.20 21:59

Also erst einmal vielen Dank an alle, dass Ihr so hilfsbereit seit! Das hilft mir sehr. Da ich nicht mit Windows und dem Internet groß geworden bin, fällt doch einiges recht schwer (Internet Recherche, englische Beiträge verstehen). Dank Eurer Hilfe finde ich jetzt schon einmal einen Weg, wie ich weiterkomme!
--
Eine Sache, die schon (oder in einem Link?) angesprochen wurde, ist die, wie man mit Hilfe von temporären Tabellen das Ergebnis von Stored Procedure's in komplexere Select Statements einbaut. Ich habe auch schon versucht, so mit temporären Tabellen zu arbeiten. Bin aber noch daran gescheitert...
Also noch eine konkrete Frage: wie kann ich das Ergebnis einer Stored Procedure in eine temporäre Tabelle speichern?


UliTs - Mo 17.02.20 22:02

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
...

FDQuery.Fields[0].AsInteger sollte die 95 ausgeben.
FDQuery.Fields[1].AsInteger sollte die 105 ausgeben.

Und so weiter. Ich dachte, die Array-Eigenschaft wäre schon Hinweis genug ;)

Hallo OlafSt,
vielen Dank. Aber ich glaube, dass Du Dich Irrst. mit .Fields[0] und .Fields[1] kommt man meines Erachtens an die Werte der ersten beiden Spalten eines Select-Ergebnisses. Wenn es mehrere Select-Mengen als Rückgabe gibt, muss es noch eine andere Lösung geben.

Moderiert von user profile iconTh69: Delphi-Tags hinzugefügt


Th69 - Di 18.02.20 10:41

Eine temporäre Tabelle wird mit dem Schlüsselwort TEMPORARY erstellt, s. MySQL Temporary Table [https://www.mysqltutorial.org/mysql-temporary-table/] bzw. CREATE TEMPORARY TABLE Statement [https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html] (auf deutsch habe ich dazu keine guten Tutorials gefunden).
Aber für nur einen Datenwert solltest du einfach aus der Prozedur eine Funktion machen.

Und zu
UliTs hat folgendes geschrieben:
Wenn es mehrere Select-Mengen als Rückgabe gibt, muss es noch eine andere Lösung geben.

Dazu wurde schon die Antwort gegeben: NextRecordSet (auch die TFDQuery [http://docwiki.embarcadero.com/Libraries/Rio/en/FireDAC.Comp.Client.TFDQuery]-Klasse hat diese Methode).


UliTs - Mi 19.02.20 12:47

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Und zu
UliTs hat folgendes geschrieben:
Wenn es mehrere Select-Mengen als Rückgabe gibt, muss es noch eine andere Lösung geben.

Dazu wurde schon die Antwort gegeben: NextRecordSet (auch die TFDQuery [http://docwiki.embarcadero.com/Libraries/Rio/en/FireDAC.Comp.Client.TFDQuery]-Klasse hat diese Methode).

Danke! Ich habe wohl den Wald vor lauter Bäumen nicht gesehen :oops: ...


user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Eine temporäre Tabelle wird mit dem Schlüsselwort TEMPORARY erstellt, s. MySQL Temporary Table [https://www.mysqltutorial.org/mysql-temporary-table/] bzw. CREATE TEMPORARY TABLE Statement [https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html] (auf deutsch habe ich dazu keine guten Tutorials gefunden).

Ich kenne es vom Advantage Database Server (ADS) so, dass ich direkt durch ein select statement das Ergebnis in eine temporäre Tabelle schreiben kann, die in dem Moment auch noch erzeugt wird. Ich vermute, dass geht in MySql nicht?


Ralf Jansen - Mi 19.02.20 21:39

Zitat:
Ich kenne es vom Advantage Database Server (ADS) so, dass ich direkt durch ein select statement das Ergebnis in eine temporäre Tabelle schreiben kann, die in dem Moment auch noch erzeugt wird. Ich vermute, dass geht in MySql nicht?


In dem 2.ten Link den du hier zitiert hast zeigt das einzige Code Beispiel genau das. Ohne den dort gezeigten Limit 0 Anhang am Statement würden natürlich auch die Daten kopiert werden die das Select Statement liefert.


UliTs - So 01.03.20 00:13

Hallo Ralf,
danke! Man, war ich blind ... :oops: