mysql stored procedures

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
silver0346
User
Beiträge: 5
Registriert: Freitag 13. Juni 2014, 21:52

Hallo zusammen,

ich versuche jetzt seit gefühlt 24 Stunden, einen Rückgabewert aus einer MySQL stored procedure in Python zu erhalten. Bisher habe ich stored procedures verwendet, die unter Perl zufriedenstellend laufen. Unter Python leider nicht. Deshalb habe ich das Beispiel von Oracle verwendet. Ist laut Website ziemlich einfach:

Code: Alles auswählen

CREATE PROCEDURE `sp1`(
	in pStr1 varchar(20),
	in pStr2 varchar(20),
	out pConCat varchar(40)
)
BEGIN
	set pConCat := CONCAT(pStr1, pStr2);
END
In Python:

Code: Alles auswählen

class DbTest(object):
    def __init__(self):
        super(DbTest, self).__init__()

    def main(self):
        config = {
            'user': 'reporting',
            'password': 'reporting',
            'host': 'host',
            'database': 'bla'
        }
        back = mysql.connector.connect(**config)
        cur = back.cursor(buffered=True)
        args = ('ham', 'spam', '')
        cur.callproc('sp1', args)
        print(cur.fetchone())
        cur.close()
        back.close()

if __name__ == '__main__':
    m = DbTest()
    m.main()
Ich kann den Vorgang im querry-log verfolgen:

Code: Alles auswählen

140613 22:28:12	   87 Connect	reporting@host on report
		   87 Query	SET NAMES 'utf8' COLLATE 'utf8_general_ci'
		   87 Query	SET @@session.autocommit = OFF
		   87 Query	SET @_sp1_arg1='ham'
		   87 Query	SET @_sp1_arg2='spam'
		   87 Query	SET @_sp1_arg3=''
		   87 Query	CALL sp1(@_sp1_arg1,@_sp1_arg2,@_sp1_arg3)
		   87 Query	SELECT @_sp1_arg1,@_sp1_arg2,@_sp1_arg3
		   87 Quit	
Wenn ich diese Zeilen in MySQL WorkBench kopiere, erhalte ich ein - erwartetes - Ergebnis. Nicht jedoch in Python: Hier ist das Ergebnis immer None.

Kann mir da jemand einen kleinen Denkanstoß geben?
Zuletzt geändert von Anonymous am Freitag 13. Juni 2014, 22:22, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

Denkanstoss wäre die Dokumentation von `callproc()` zu konsultieren: http://legacy.python.org/dev/peps/pep-0249/#callproc

Deine Prozedur stellt ja nichts bereit was man mit `fetchone()` auslesen könnte, sondern hat einen ``out``-Parameter.

Die Klasse ist übrigens unsinnig, da hätte es auch eine einfache Funktion getan. Und von `super()` würde ich die Finger lassen.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Probier mal das

Code: Alles auswählen

args = ['ham', 'spam', ''] # eine list darf veraendert werden, ein tuple nicht
cur.callproc('sp1', args)
print(args) # eventuell ist in args[2] der out-Parameter
BlackJack hat geschrieben:Die Klasse ist übrigens unsinnig, da hätte es auch eine einfache Funktion getan.
In Java muss man das so machen.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

MagBen hat geschrieben:
BlackJack hat geschrieben:Die Klasse ist übrigens unsinnig, da hätte es auch eine einfache Funktion getan.
In Java muss man das so machen.
Was hat Java damit zu tun :K Der OP sprach lediglich von Perl... und da braucht 's sicher auch keine Klasse :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
silver0346
User
Beiträge: 5
Registriert: Freitag 13. Juni 2014, 21:52

Moin moin,

vielen Dank für die schnelle Antwort.

Mit dem Perl-Zitat wollte ich nur andeuten, dass die stored procedures funktionieren. Die Doku zu callproc kenne ich inzwischen wohl auswendig (ist ja auch nicht so lang). Ich habe mir sogar schon den Quelltext für das entsprechende Modul angesehen. Man kann ja nie wissen...

Wie aus dem query-log hervor geht, wird auf jeden Fall etwas für ein fetch*() bereitgestellt: Der letzte Befehl ist ein SELECT, der alle Parameter ausgibt (auch den gefüllten out-Parameter). In reinem SQL sieht man es. Nur sämtliche fetch-Varianten finden nichts. Auch der Vorschlag von MagBen bringt leider nicht den gewünschten Erfolg.

Und wieso ist super() böse?

Ich such mal noch ein wenig weiter...
BlackJack

Mal die beiden relevanten Sätze aus dem Dokument zu `callproc()`:
The result of the call is returned as modified copy of the input sequence. Input parameters are left untouched, output and input/output parameters replaced with possibly new values.
Und etwas später dann:
The procedure may also provide a result set as output. This must then be made available through the standard .fetch*() methods.
Was zusammengenommen IMHO ziemlich deutlich darauf hinweist, dass man sich mal den Rückgabewert der Methode anschauen sollte, und dass das auch eher der Regelfall gegenüber einem „result set” ist.

Zu `super()`: Python's Super is nifty, but you can't use it. Mir ist das einfach zu kompliziert und ich habe es auch noch nie in eigenem Code *gebraucht*. Denn wirklich nützen tut das ja erst wenn man Mehrfachvererbung praktiziert die nicht aus ”Mixins” besteht. Und das macht meiner Erfahrung nach sowieso niemand.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

silver0346 hat geschrieben: Denn wirklich nützen tut das ja erst wenn man Mehrfachvererbung praktiziert die nicht aus ”Mixins” besteht.
Du brauchst super immer dann, wenn in der abgeleiteten Klasse das Verhalten der Parent-Klasse erweitert werden soll. Und wenn Du in Qt von einer QObject oder QWidget Klasse ableitest und im eigenen Konstruktor nicht super(..., self).__init__(...) aufrufst, dann funktioniert gar nichts mehr.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Die Alternative zu `super` ist nicht, dass man die Eltern-Methode gar nicht aufruft, sondern dass man es explizit tut. In dem Fall wuerde man eben `QObject.__init__(self, ...)` aufrufen.

Und bei quoten aufpassen, dass du auch den richtigen Autor triffst ;)
BlackJack

@MagBen: Das stimmt so nicht denn man kann die entsprechende Methode der Basisklasse auch problemlos ohne `super()` aufrufen. `super()` ruft nicht die Methode der Basisklasse auf, sondern die nächste in der „method resolution order” (MRO). Das muss nicht die Methode in der Basisklasse sein. Denn wenn sie das immer wäre, dann gäbe es überhaupt keinen Grund für die Existenz der Funktion. Und ob man die nächste Methode in der MRO aufrufen muss, statt einfach nur die in der Basisklasse, hängt davon ab ob man sich irgendwelche spassigen „diamantförmigen” Vererbungsbeziehungen mittels Mehrfachvererbung gebastelt hat. So etwas mache ich nicht, und soweit ich bisher Python-Quelltext gesehen habe, macht das auch niemand anders der Wert auf seine geistige Gesundheit legt. Ergo: Wenn man keine Mehrfachvererbung abseits von ”Mixins” benutzt, ist die ganze Komplexität und die Konsequenzen die man sich mit `super()` einfängt, völlig unnötig.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Hyperion hat geschrieben:Was hat Java damit zu tun
Keine Ahnung warum dieses Problem etwas mit Java zu tun hat. Mir ist lediglich aufgefallen, dass da jemand Python wie Java programmiert.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BlackJack hat geschrieben:Wenn man keine Mehrfachvererbung abseits von ”Mixins” benutzt, ist die ganze Komplexität und die Konsequenzen die man sich mit `super()` einfängt, völlig unnötig.
Ich finde super nicht besonders komplex. Ich habe es in Java sehr gerne benutzt und ich vermisse es in C++.

super() ist immer dann super, wenn sich die Vererbunghierarchie im Lauf der Zeit entwickelt. Klasse C wird von Klasse A abgeleitet, ein Jahr später fummele ich da noch eine Klasse B dazwischen, sodass C von B und B von A abgeleitet ist. Mit super ruft C in der ersten Version Klasse A auf und später dann Klasse B. Hätte ich Klasse A in C explizit aufgerufen, dann würde ich mich später wundern, warum meine Klasse B keine Auswirkung hat.
a fool with a tool is still a fool, www.magben.de, YouTube
silver0346
User
Beiträge: 5
Registriert: Freitag 13. Juni 2014, 21:52

Wieder zurück. :wink:

Ich kann gar nicht java-Programmierung. Das Einzige, was mir - mit Google und Co. - gelingt ist Perl. Jetzt versuche ich mich an Python, indem ich bisher als Perl-Scripte laufende Sachen umschreibe. Unter anderem, weil ich recht leicht eine GUI zusammenklöppeln kann. Unter Perl war da etwas mehr Aufwand nötig. Aber zurück zum Thema:

Ich habe etwas im Quelltext probiert. Dabei ist mir folgendes aufgefallen: In cursor.py lautet das Original in callproc:

Code: Alles auswählen

if argnames:
    select = "SELECT {0}".format(','.join(argtypes))
    self.execute(select)
    self._stored_results = results
    return self.fetchone()
else:
    self._stored_results = results
    return ()
Dabei kommt nichts zurück. Ersetze ich jedoch fetchone() durch fetchall()[0] kriege ich ein Tupel mit genau den Werten, die ich haben will.

Code: Alles auswählen

(u'ham', u'spam', 'hamspam')
Und da ich weiß, an welcher Stelle mein Rückgabewert stehen soll, kann ich mit

Code: Alles auswählen

res = self.cur.callproc('sp1', args)
print(res[2])
auf die Zelle zugreifen. Ich habe zwar noch nicht probiert, ob es in allen Fällen zufriedenstellend funktioniert, aber es scheint der richtige Weg zu sein. Warum fetchone() nichts liefert ist mir noch nicht ganz klar. Komme ich aber noch hinter. :D
Antworten