¿Cómo puedo definir una función o procedimiento de múltiples declaraciones al usar la biblioteca MySQLdb en python?

Ejemplo:

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

Lo que crea el siguiente rastreo:

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

Si copio el mismo SQL directamente en un cliente de shell mysql, funciona como se esperaba

15
ʞɔıu 14 abr. 2009 a las 01:54

3 respuestas

La mejor respuesta

El comando DELIMITER es un cliente de shell de MySQL integrado, y solo es reconocido por ese programa (y MySQL Query Browser). No es necesario usar DELIMITER si ejecuta instrucciones SQL directamente a través de una API.

El propósito de DELIMITER es ayudarlo a evitar la ambigüedad sobre la terminación de la declaración CREATE FUNCTION, cuando la declaración en sí misma puede contener caracteres de punto y coma. Esto es importante en el cliente de shell, donde por defecto un punto y coma termina una instrucción SQL. Debe establecer el terminador de la declaración en algún otro carácter para enviar el cuerpo de una función (o desencadenador o procedimiento).

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

Dado que la API generalmente le permite enviar una declaración SQL a la vez, no hay ambigüedad: la interfaz sabe que cualquier punto y coma dentro del cuerpo de su definición de función no termina la declaración CREATE FUNCTION completa. Por lo tanto, no es necesario cambiar el terminador de la declaración con DELIMITER.

20
Bill Karwin 13 abr. 2009 a las 22:15

Para agregar a la respuesta de Bill Karwin, la siguiente muestra de código de Python se puede usar para ejecutar correctamente una cadena donde se usa DELIMITER, como un script de creación de base de datos.

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)

Esto ejecutará una declaración delimitada a la vez, cambiando los delimitadores cuando sea necesario.

9
nawsleahcimnoraa 29 abr. 2016 a las 02:53

Basado en el comentario de @AaronS. Este script leerá en un archivo SQL, lo dividirá en comandos SQL discretos y hará frente a los delimitadores que encuentre.

    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()
3
DenisH 6 feb. 2015 a las 14:39