如何在 python 中使用 MySQLdb 库定义多语句函数或过程?
示例:
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 ;""")
这会创建以下回溯:
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")
如果我将相同的 SQL 直接复制到 mysql shell 客户端,它会按预期工作
DELIMITER
命令是内置的MySQL shell客户端,它只能被该程序(和MySQL查询浏览器)识别。如果直接通过 API 执行 SQL 语句,则无需使用 DELIMITER
。
DELIMITER
的目的是帮助您避免在 CREATE FUNCTION
语句终止时出现歧义(当语句本身可以包含分号字符时)。这在 shell 客户端中很重要,默认情况下分号终止 SQL 语句。您需要将语句终止符设置为其他字符才能提交函数(或触发器或过程)的主体。
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
由于 API 通常允许您一次提交一个 SQL 语句,因此不会有任何歧义——接口知道函数定义体内的任何分号都不会终止整个
CREATE FUNCTION
语句。所以不需要用 DELIMITER
来改变语句终止符。
要添加 Bill Karwin 的答案,可以使用以下 python 代码示例来正确执行使用 DELIMITER 的字符串,例如数据库创建脚本。
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)
这将一次执行一个分隔语句,并在需要时更改分隔符。
基于@AaronS 的评论。该脚本将读取 SQL 文件,将其拆分为离散的 SQL 命令并处理它找到的任何分隔符。
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()
基于@DenisH 的评论。它可以解决一个 SQL 命令字符串中指定的多个语句。
def execute_muti(cursor, sql_commands):
queries = []
delimiter = ';'
query = ''
for line in sql_commands.split('\n'):
line = line.strip()
if line.lower().startswith('delimiter'): # Find special delimiters
delimiter = line[10:].strip()
else:
query += line + '\n'
if line.endswith(delimiter):
query = query.strip()[:-len(delimiter)]
queries.append(query)
query = ''
for query in queries:
if not query.strip():
continue
results = cursor.execute(query, multi=True)
for result in results:
if result.with_rows:
print("Rows produced by statement '{}':".format(result.statement))
print(result.fetchall())
else:
print("Number of rows affected by statement '{}': {}".format(
result.statement,
result.rowcount
))
以下示例展示了如何使用它:
import mysql.connector
with mysql.connector.connect(host='localhost', port=3306, user='root', password='test') as db:
with db.cursor() as cursor:
sql = 'INSERT YOUR SQL CODE HERE!!'
execute_muti(cursor, sql)