【问题标题】:Create function through MySQLdb通过 MySQLdb 创建函数
【发布时间】:2009-04-13 21:54:10
【问题描述】:

如何在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


    【解决方案1】:

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

    【讨论】:

    • 除非你需要。例如运行数据库创建脚本。有什么方法可以将原始 SQL 传递给 MySql?
    • @BryanHunt,你不能通过动态 SQL API 提交任意 SQL 脚本,因为有几个命令只能被 mysql 客户端识别,而不是服务器 SQL 解析器。
    • 09 年已回答,但仍在努力!谢谢!!
    【解决方案2】:

    为了补充 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)
    

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

    【讨论】:

    • 这对我有用。我还需要添加query = query.replace('%', '%%'),以在我的 .sql 文件中转义 % 字符。
    【解决方案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()
    

    【讨论】:

      猜你喜欢
      • 2014-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多