在python中运行多个sql语句的建议方法?

Dav*_*542 32 python mysql mysql-python

在 python 中运行类似以下内容的建议方法是什么:

self.cursor.execute('SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS %s; SET FOREIGN_KEY_CHECKS=1' % (table_name,))
Run Code Online (Sandbox Code Playgroud)

例如,这应该是三个单独的self.cursor.execute(...)语句吗?除了cursor.execute(...)做这样的事情之外,是否应该使用特定的方法,或者这样做的建议做法是什么?目前我的代码如下:

self.cursor.execute('SET FOREIGN_KEY_CHECKS=0;')
self.cursor.execute('DROP TABLE IF EXISTS %s;' % (table_name,))
self.cursor.execute('SET FOREIGN_KEY_CHECKS=1;')
self.cursor.execute('CREATE TABLE %s select * from mytable;' % (table_name,))
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,一切都是单独运行的……所以我不确定这是否是一个好主意(或者更确切地说 - 执行上述操作的最佳方法是什么)。也许BEGIN...END

Dan*_*n M 12

我会创建一个存储过程:

DROP PROCEDURE IF EXISTS CopyTable;
DELIMITER $$
CREATE PROCEDURE CopyTable(IN _mytable VARCHAR(64), _table_name VARCHAR(64))
BEGIN
    SET FOREIGN_KEY_CHECKS=0;
    SET @stmt = CONCAT('DROP TABLE IF EXISTS ',_table_name);
    PREPARE stmt1 FROM @stmt;
    EXECUTE stmt1;
    SET FOREIGN_KEY_CHECKS=1;
    SET @stmt = CONCAT('CREATE TABLE ',_table_name,' as select * from ', _mytable);
    PREPARE stmt1 FROM @stmt;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;
END$$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

然后运行:

args = ['mytable', 'table_name']
cursor.callproc('CopyTable', args)
Run Code Online (Sandbox Code Playgroud)

保持简单和模块化。当然,您应该进行某种错误检查,甚至可以让存储过程返回一个代码来指示成功或失败。


Inf*_*ity 11

在 的文档中MySQLCursor.execute(),他们建议使用multi=True参数:

operation = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2'
for result in cursor.execute(operation, multi=True):
    ...
Run Code Online (Sandbox Code Playgroud)

您可以在模块的源代码中找到另一个示例


Shu*_*pta 5

我在项目中多次陷入此类问题。经过大量研究后,我发现了一些观点和建议。

  1. execute()该方法一次可以很好地处理一个查询。因为在执行方法期间会照顾状态。

我知道cursor.execute(operation, params=None, multi=True)需要进行多次查询。但参数在这种情况下不能很好地工作,有时内部错误异常也会破坏所有结果。代码变得庞大且含糊不清。甚至文档也提到了这一点。 在此输入图像描述

  1. executemany(operation, seq_of_params)并不是每次都实施的好习惯。因为产生一个或多个结果集的操作构成未定义的行为,并且当实现检测到已通过操作的调用创建结果集时,允许(但不要求)引发异常。[来源-文档]

建议1-:

制作一个查询列表,例如 -:

table_name = 'test'
quries = [
          'SET FOREIGN_KEY_CHECKS=0;',
          'DROP TABLE IF EXISTS {};'.format(table_name),
          'SET FOREIGN_KEY_CHECKS=1;',
          'CREATE TABLE {} select * from mytable;'.format(table_name),
         ]
for query in quries:
    result = self.cursor.execute(query)
    # Do operation with result
Run Code Online (Sandbox Code Playgroud)

建议2-:

用字典设置。[you can also make this by executemany for recursive parameters for some special cases.]

quries = [
          {'DROP TABLE IF EXISTS %(table_name);':{'table_name': 'student'}},
          {'CREATE TABLE %(table_name) select * from mytable;': 
           {'table_name':'teacher'}},
          {'SET FOREIGN_KEY_CHECKS=0;': ''}
         ]
for data in quries:
    for query, parameter in data.iteritems():
        if parameter == '':
            result = self.cursor.execute(query)
            # Do something with result
        else:
            result = self.cursor.execute(query, parameter)
            # Do something with result

Run Code Online (Sandbox Code Playgroud)

您还可以将 split 与脚本一起使用。Not recommended

with connection.cursor() as cursor:
    for statement in script.split(';'):
        if len(statement) > 0:
             cursor.execute(statement + ';')
Run Code Online (Sandbox Code Playgroud)

注意-:我主要使用list of query方法,但在一些复杂的地方使用 make dictionary方法。


Boo*_*boo 5

我不会依赖multi=True该函数的任何参数execute,这非常依赖于驱动程序,也不会尝试尝试拆分字符上的字符串;,该字符可能嵌入在字符串文字中。最直接的方法是创建一个函数 ,execute_multiple它接受要执行的语句列表和一个rollback_on_error参数,以确定在任何语句导致异常时要执行的操作。

我使用 MySQLdb 和 PyMySQL 的经验是,默认情况下它们从 开始autocommit=0,换句话说,就好像您已经处于事务中并且需要显式提交。无论如何,这个假设适用于下面的代码。如果不是这种情况,那么您应该 1. 在连接后显式设置或 2. 修改此代码以在语句autocommit=0后面启动事务try

def execute_multiple(conn, statements, rollback_on_error=True):
    """
    Execute multiple SQL statements and returns the cursor from the last executed statement.

    :param conn: The connection to the database
    :type conn: Database connection

    :param statements: The statements to be executed
    :type statements: A list of strings

    :param: rollback_on_error: Flag to indicate action to be taken on an exception
    :type rollback_on_error: bool

    :returns cursor from the last statement executed
    :rtype cursor
    """

    try:
        cursor = conn.cursor()
        for statement in statements:
            cursor.execute(statement)
            if not rollback_on_error:
                conn.commit() # commit on each statement
    except Exception as e:
        if rollback_on_error:
            conn.rollback()
        raise
    else:
        if rollback_on_error:
            conn.commit() # then commit only after all statements have completed successfully
Run Code Online (Sandbox Code Playgroud)

您还可以拥有一个使用其参数列表处理准备好的语句的版本:

def execute_multiple_prepared(conn, statements_and_values, rollback_on_error=True):
    """
    Execute multiple SQL statements and returns the cursor from the last executed statement.

    :param conn: The connection to the database
    :type conn: Database connection

    :param statements_and_values: The statements and values to be executed
    :type statements_and_values: A list of lists. Each sublist consists of a string, the SQL prepared statement with %s placeholders, and a list or tuple of its parameters

    :param: rollback_on_error: Flag to indicate action to be taken on an exception
    :type rollback_on_error: bool

    :returns cursor from the last statement executed
    :rtype cursor
    """

    try:
        cursor = conn.cursor()
        for s_v in statements_and_values:
            cursor.execute(s_v[0], s_v[1])
            if not rollback_on_error:
                conn.commit() # commit on each statement
    except Exception as e:
        if rollback_on_error:
            conn.rollback()
        raise
    else:
        if rollback_on_error:
            conn.commit() # then commit only after all statements have completed successfully
        return cursor # return the cursor in case there are results to be processed
Run Code Online (Sandbox Code Playgroud)

例如:

cursor = execute_multiple_prepared(conn, [('select * from test_table where count = %s', (2000,))], False)
Run Code Online (Sandbox Code Playgroud)

尽管不可否认,上述调用只有一个带参数的 SQL 准备语句。


Fir*_*ger 5

情人眼里出西施,所以做某事的最佳方法是主观的,除非你明确告诉我们如何衡量。我可以看到三个假设选项:

  1. 使用multiMySQLCursor的选项(不理想)
  2. 将查询保留在多行中
  3. 将查询保留在单行中

或者,您还可以更改查询以避免一些不必要的工作。


关于该multi选项,MySQL 文档对此非常清楚

如果 multi 设置为 True,execute() 能够执行操作字符串中指定的多个语句。它返回一个迭代器,可以处理每个语句的结果。但是,在这种情况下使用参数效果不佳,单独执行每个语句通常是一个好主意


关于选项 2. 和 3.,这纯粹是您希望如何查看代码的偏好。回想一下,连接对象autocommit=FALSE默认具有连接对象,因此游标实际上cursor.execute(...)将调用批处理到单个事务中。换句话说,下面的两个版本是等效的。

self.cursor.execute('SET FOREIGN_KEY_CHECKS=0;')
self.cursor.execute('DROP TABLE IF EXISTS %s;' % (table_name,))
self.cursor.execute('SET FOREIGN_KEY_CHECKS=1;')
self.cursor.execute('CREATE TABLE %s select * from mytable;' % (table_name,))
Run Code Online (Sandbox Code Playgroud)

self.cursor.execute(
    'SET FOREIGN_KEY_CHECKS=0;'
    'DROP TABLE IF EXISTS %s;' % (table_name,)
    'SET FOREIGN_KEY_CHECKS=1;'
    'CREATE TABLE %s select * from mytable;' % (table_name,)
)
Run Code Online (Sandbox Code Playgroud)

Python 3.6 引入了超级优雅的 f 字符串,如果可以的话你应该使用它们。:)

self.cursor.execute('SET FOREIGN_KEY_CHECKS=0;')
self.cursor.execute('DROP TABLE IF EXISTS %s;' % (table_name,))
self.cursor.execute('SET FOREIGN_KEY_CHECKS=1;')
self.cursor.execute('CREATE TABLE %s select * from mytable;' % (table_name,))
Run Code Online (Sandbox Code Playgroud)

请注意,当您开始操作行时,这不再成立;在这种情况下,它变得特定于查询,您应该分析相关的内容。一个相关的问题是,一个大查询或许多小查询哪个更快?


最后,除非您有特定的理由不这样TRUNCATE做,否则使用它可能会更优雅。DROP TABLE

self.cursor.execute(
    'SET FOREIGN_KEY_CHECKS=0;'
    'DROP TABLE IF EXISTS %s;' % (table_name,)
    'SET FOREIGN_KEY_CHECKS=1;'
    'CREATE TABLE %s select * from mytable;' % (table_name,)
)
Run Code Online (Sandbox Code Playgroud)