通过MySQLdb创建函数

问题描述 投票:0回答:4

如何在 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 客户端,它会按预期工作

python mysql
4个回答
21
投票

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
来改变语句终止符。


11
投票

要添加 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)

这将一次执行一个分隔语句,并在需要时更改分隔符。


3
投票

基于@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()

0
投票

基于@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)
© www.soinside.com 2019 - 2024. All rights reserved.