2009-04-13 9 views
13

Wie kann ich eine Multi-Anweisung-Funktion oder Prozedur bei der Verwendung der MySQLdb-Lib in Python definieren?Erstellen Sie Funktion über MySQLdb

Beispiel:

import MySQLdb 

db = MySQLdb.connect(db='service') 

c = db.cursor() 

c.execute("""DELIMITER // 
CREATE FUNCTION trivial_func (radius float) 
    RETURNS FLOAT 

    BEGIN 
    IF radius > 1 THEN 
     RETURN 0.0; 
    ELSE 
     RETURN 1.0; 
    END IF; 
END // 

DELIMITER ;""") 

Welche der folgenden Zurückverfolgungs erstellt:

Traceback (most recent call last): 
    File "proof.py", line 21, in <module> 
    DELIMITER ;""") 
    File "build/bdist.macosx-10.5-i386/egg/MySQLdb/cursors.py", line 173, in execute 
    File "build/bdist.macosx-10.5-i386/egg/MySQLdb/connections.py", line 35, in defaulterrorhandler 
_mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //\nCREATE FUNCTION trivial_func (radius float) \n RETURNS FLOAT\n\n ' at line 1") 

Wenn ich die gleiche SQL direkt in einer MySQL-Shell-Client kopieren, wie erwartet

arbeitet

Antwort

17

Der DELIMITER Befehl ist ein eingebauter MySQL Shell Client und wird nur von diesem Programm (und dem MySQL Query Browser) erkannt. Wenn Sie SQL-Anweisungen direkt über eine API ausführen, ist es nicht erforderlich, DELIMITER zu verwenden.

Der Zweck von DELIMITER ist es, Ihnen zu helfen, Mehrdeutigkeit über die Beendigung der CREATE FUNCTION-Anweisung zu vermeiden, wenn die Anweisung selbst Semikolonzeichen enthalten kann. Dies ist wichtig im Shell-Client, wo standardmäßig ein Semikolon eine SQL-Anweisung beendet. Sie müssen das Anweisungsabschlusszeichen auf ein anderes Zeichen setzen, um den Rumpf einer Funktion (oder eines Triggers oder einer Prozedur) zu übergeben.

CREATE FUNCTION trivial_func (radius float) 
    RETURNS FLOAT 

    BEGIN 
    IF radius > 1 THEN 
     RETURN 0.0; <-- does this semicolon terminate RETURN or CREATE FUNCTION? 
    ELSE 
     RETURN 1.0; 
    END IF; 
END 

Da die API der Regel können Sie eine SQL-Anweisung zu einem Zeitpunkt einzureichen, gibt es keine Zweideutigkeit - die Schnittstelle weiß, dass alle Semikolons im Inneren des Körpers Ihrer Funktionsdefinition nicht die ganze CREATE FUNCTION Aussage beenden. Es ist also nicht notwendig, den Anweisungsabschluss mit DELIMITER zu ändern.

+0

wenn Sie nicht müssen. Führen Sie beispielsweise ein Skript zur Datenbankerstellung aus. Gibt es irgendeine Möglichkeit, nur SQL-Rohdaten an MySql weiterzuleiten? –

+0

@BryanHunt, Sie können kein beliebiges SQL-Skript über die dynamische SQL-API senden, da mehrere Befehle nur vom mysql-Client und nicht vom SQL-Parser des Servers erkannt werden. –

+0

Bill, danke für die Klarstellung. B –

4

Um die Antwort von Bill Karwin hinzuzufügen, kann das folgende Python-Codebeispiel verwendet werden, um eine Zeichenfolge, in der DELIMITER verwendet wird, ordnungsgemäß auszuführen, z. B. ein Skript zur Datenbankerstellung.

import MySQLdb 

db = MySQLdb.connect(db='service') 
cursor = db.cursor() 

dbString = """DELIMITER // 
CREATE FUNCTION trivial_func (radius float) 
RETURNS FLOAT 

BEGIN 
IF radius > 1 THEN 
    RETURN 0.0; 
ELSE 
    RETURN 1.0; 
END IF; 
END // 

DELIMITER ;""" 

# Find special delimiters 
delimiters = re.compile('DELIMITER *(\S*)',re.I) 
result = delimiters.split(dbString) 

# Insert default delimiter and separate delimiters and sql 
result.insert(0,';') 
delimiter = result[0::2] 
section = result[1::2] 

# Split queries on delimiters and execute 
for i in range(len(delimiter)): 
    queries = section[i].split(delimiter[i]) 
    for query in queries: 
     if not query.strip(): 
      continue 
     cursor.execute(query) 

Dadurch wird jeweils eine getrennte Anweisung ausgeführt, wobei die Begrenzungszeichen bei Bedarf geändert werden.

+0

Das hat den Trick für mich gemacht. Ich musste auch 'query = query.replace ('%', '%%')' hinzufügen, um% Zeichen in meiner .sql Datei zu umgehen. – Lundy

0

Basierend auf dem Kommentar von @AaronS. Dieses Skript liest eine SQL-Datei ein, teilt sie in diskrete SQL-Befehle auf und bewältigt alle gefundenen Trennzeichen.

queries = [] 
    delimiter = ';' 
    query = '' 
    with open('import.sql', 'r') as f: 
     for line in f.readlines(): 
      line = line.strip() 
      if line.startswith('DELIMITER'): 
       delimiter = line[10:] 
      else: 
       query += line+'\n' 
       if line.endswith(delimiter): 
        # Get rid of the delimiter, remove any blank lines and add this query to our list 
        queries.append(query.strip().strip(delimiter)) 
        query = '' 

    for query in queries: 
     if not query.strip(): 
      continue 
     cursor.execute(query) 
    cursor.close()