2009-04-13 14 views
13

Come definire una funzione o una procedura a più istruzioni nell'uso della libreria MySQLdb in python?Creazione della funzione tramite MySQLdb

Esempio:

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 ;""") 

che crea il seguente traceback:

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") 

Se copio lo stesso SQL direttamente in un client di shell mysql, funziona come previsto

risposta

17

Il comando DELIMITER è un client shell MySQL incorporato ed è riconosciuto solo da quel programma (e MySQL Query Browser). Non è necessario utilizzare DELIMITER se si eseguono istruzioni SQL direttamente tramite un'API.

Lo scopo di DELIMITER è quello di aiutare a evitare ambiguità circa la cessazione della dichiarazione CREATE FUNCTION, quando l'istruzione stessa può contenere caratteri punto e virgola. Questo è importante nel client di shell, dove per impostazione predefinita un punto e virgola termina un'istruzione SQL. È necessario impostare il terminatore dell'istruzione su un altro carattere per inviare il corpo di una funzione (o trigger o procedura).

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 

Dal momento che l'API consente in genere di presentare una dichiarazione di SQL alla volta, non c'è alcuna ambiguità - l'interfaccia sa che ogni punto e virgola all'interno del corpo della vostra definizione di funzione non terminano l'intero CREATE FUNCTION dichiarazione. Quindi non è necessario modificare il terminatore dell'istruzione con DELIMITER.

+0

a meno che non sia necessario. ad esempio, eseguire uno script di creazione del database. c'è un modo per passare semplicemente SQL raw a MySql? –

+0

@BryanHunt, non è possibile inviare uno script SQL arbitrario tramite l'API SQL dinamica, poiché diversi comandi sono riconosciuti solo dal client 'mysql', non dal parser SQL del server. –

+0

Bill, grazie per i chiarimenti. B –

4

Per aggiungere alla risposta di Bill Karwin, è possibile utilizzare il seguente esempio di codice python per eseguire correttamente una stringa in cui viene utilizzato DELIMITER, ad esempio uno script di creazione del database.

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) 

Questo eseguirà una dichiarazione delimitata alla volta, modificando i delimitatori quando necessario.

+0

Questo ha fatto il trucco per me. Avevo anche bisogno di aggiungere 'query = query.replace ('%', '%%')', per sfuggire% caratteri nel mio file .sql. – Lundy

0

Sulla base del commento di @AaronS. Questo script leggerà in un file SQL, dividerlo in comandi SQL discreti e far fronte a qualsiasi delimitatore che trova.

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() 
Problemi correlati