使用python MySQLdb执行* .sql文件

时间:2010-12-10 12:13:29

标签: python mysql

如何使用MySQLdb python驱动程序执行存储在* .sql文件中的sql脚本。我在尝试


cursor.execute(file(PATH_TO_FILE).read())

但这不起作用,因为 cursor.execute 一次只能运行一个sql命令。我的sql脚本包含几个sql语句。我也在尝试


cursor.execute('source %s'%PATH_TO_FILE)

但也没有成功。

15 个答案:

答案 0 :(得分:33)

从python中,我启动一个mysql进程来为我执行该文件:

from subprocess import Popen, PIPE
process = Popen(['mysql', db, '-u', user, '-p', passwd],
                stdout=PIPE, stdin=PIPE)
output = process.communicate('source ' + filename)[0]

答案 1 :(得分:14)

我还需要执行一个SQL文件,但问题是每行没有一个语句,所以接受的答案对我不起作用。

我想要执行的SQL文件如下所示:

-- SQL script to bootstrap the DB:
--
CREATE USER 'x'@'%' IDENTIFIED BY 'x';
GRANT ALL PRIVILEGES ON mystore.* TO 'x'@'%';
GRANT ALL ON `%`.* TO 'x'@`%`;
FLUSH PRIVILEGES;
--
--
CREATE DATABASE oozie;
GRANT ALL PRIVILEGES ON oozie.* TO 'oozie'@'localhost' IDENTIFIED BY 'oozie';
GRANT ALL PRIVILEGES ON oozie.* TO 'oozie'@'%' IDENTIFIED BY 'oozie';
FLUSH PRIVILEGES;
--
USE oozie;
--
CREATE TABLE `BUNDLE_ACTIONS` (
  `bundle_action_id` varchar(255) NOT NULL,
  `bundle_id` varchar(255) DEFAULT NULL,
  `coord_id` varchar(255) DEFAULT NULL,
  `coord_name` varchar(255) DEFAULT NULL,
  `critical` int(11) DEFAULT NULL,
  `last_modified_time` datetime DEFAULT NULL,
  `pending` int(11) DEFAULT NULL,
  `status` varchar(255) DEFAULT NULL,
  `bean_type` varchar(31) DEFAULT NULL,
  PRIMARY KEY (`bundle_action_id`),
  KEY `I_BNDLTNS_DTYPE` (`bean_type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
--

上面文件中的一些语句位于一行,一些语句也跨越多行(如末尾的CREATE TABLE)。还有一些SQL内联注释行以“ - ”开头。

正如ThomasK所建议的,我必须编写一些简单的规则来将行连接到一个语句中。我最终得到了一个执行sql文件的函数:

def exec_sql_file(cursor, sql_file):
    print "\n[INFO] Executing SQL script file: '%s'" % (sql_file)
    statement = ""

    for line in open(sql_file):
        if re.match(r'--', line):  # ignore sql comment lines
            continue
        if not re.search(r'[^-;]+;', line):  # keep appending lines that don't end in ';'
            statement = statement + line
        else:  # when you get a line ending in ';' then exec statement and reset for next statement
            statement = statement + line
            #print "\n\n[DEBUG] Executing SQL statement:\n%s" % (statement)
            try:
                cursor.execute(statement)
            except (OperationalError, ProgrammingError) as e:
                print "\n[WARN] MySQLError during execute statement \n\tArgs: '%s'" % (str(e.args))

            statement = ""

我确信还有改进的余地,但现在它对我来说效果很好。希望有人觉得它很有用。

答案 2 :(得分:9)

for line in open(PATH_TO_FILE):
    cursor.execute(line)

这假设您的文件中每行有一个SQL语句。否则你需要编写一些规则来连接线。

答案 3 :(得分:8)

至少MySQLdb 1.2.3似乎允许开箱即用,你只需要调用cursor.nextset()来循环返回结果集。

db = conn.cursor()
db.execute('SELECT 1; SELECT 2;')

more = True
while more:
    print db.fetchall()
    more = db.nextset()

如果您想完全确定已启用对此的支持,和/或禁用支持,您可以使用以下内容:

MYSQL_OPTION_MULTI_STATEMENTS_ON = 0
MYSQL_OPTION_MULTI_STATEMENTS_OFF = 1

conn.set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON)
# Multiple statement execution here...
conn.set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_OFF)

答案 4 :(得分:7)

这对我有用:

with open('schema.sql') as f:
    cursor.execute(f.read().decode('utf-8'), multi=True)

答案 5 :(得分:4)

允许在没有任何解析的情况下利用MySQL解释器的另一个解决方案是使用os.system命令直接在python中运行MySQL提示命令:

from os import system
USERNAME = "root"
PASSWORD = "root"
DBNAME = "pablo"
HOST = "localhost"
PORT = 3306
FILE = "file.sql"
command = """mysql -u %s -p"%s" --host %s --port %s %s < %s""" %(USERNAME, PASSWORD, HOST, PORT, DBNAME, FILE)
system(command)

它可以避免任何解析错误,例如,如果你有一个带有笑脸;-)的字符串变量,或者你检查;作为最后一个字符,如果你之后有评论{{ {1}}

答案 6 :(得分:3)

当您的sql脚本包含空行并且您的查询语句跨越多行时,接受的答案将遇到问题。相反,使用以下方法将解决问题:

f = open(filename, 'r')
query = " ".join(f.readlines())
c.execute(query)

答案 7 :(得分:3)

这里的许多答案都有严重的缺陷......

首先不要尝试自己解析开放式的sql脚本!如果你认为这很容易做到,你就不会知道sql有多强大和复杂。严重的SQL脚本肯定涉及跨越多行的语句和过程定义。在脚本中间显式声明和更改分隔符也很常见。您还可以在彼此之间嵌套源命令。出于这么多原因,您希望通过MySQL客户端运行脚本并允许它处理繁重的工作。试图重新发明这是一个充满危险和巨大浪费时间的事情。也许如果你是唯一一个编写这些脚本的人,并且你没有写任何复杂的东西,你可以侥幸逃脱,但为什么要限制自己到这样的程度?机器生成的脚本或其他开发人员编写的脚本怎么样?

@jdferreira的回答是正确的,但也有问题和弱点。最重要的是通过以这种方式将连接参数发送到进程来打开安全漏洞。

以下是您的副本和解决方案的解决方案/示例粘贴乐趣。我的扩展讨论如下:

首先,创建一个单独的配置文件以保存您的用户名和密码。

<强> DB-creds.cfg

[client]
user     = XXXXXXX
password = YYYYYYY

对该文件系统权限执行权限,因此python进程可以从中读取,但没有人可以查看谁不应该这样做。

然后,使用这个Python(在我的示例中,creds文件与py脚本相邻):

#!/usr/bin/python

import os
import sys
import MySQLdb
from subprocess import Popen, PIPE, STDOUT

__MYSQL_CLIENT_PATH = "mysql"

__THIS_DIR = os.path.dirname( os.path.realpath( sys.argv[0] ) )

__DB_CONFIG_PATH    = os.path.join( __THIS_DIR, "db-creds.cfg" )
__DB_CONFIG_SECTION = "client"

__DB_CONN_HOST = "localhost"
__DB_CONN_PORT = 3306

# ----------------------------------------------------------------

class MySqlScriptError( Exception ):

    def __init__( self, dbName, scriptPath, stdOut, stdErr ):
        Exception.__init__( self )
        self.dbName = dbName
        self.scriptPath = scriptPath
        self.priorOutput = stdOut
        self.errorMsg = stdErr                
        errNumParts = stdErr.split("(")        
        try : self.errorNum = long( errNumParts[0].replace("ERROR","").strip() )
        except: self.errorNum = None        
        try : self.sqlState = long( errNumParts[1].split(")")[0].strip() )
        except: self.sqlState = None

    def __str__( self ): 
        return ("--- MySqlScriptError ---\n" +
                "Script: %s\n" % (self.scriptPath,) +
                "Database: %s\n" % (self.dbName,) +
                self.errorMsg ) 

    def __repr__( self ): return self.__str__()

# ----------------------------------------------------------------

def databaseLoginParms() :        
    from ConfigParser import RawConfigParser
    parser = RawConfigParser()
    parser.read( __DB_CONFIG_PATH )   
    return ( parser.get( __DB_CONFIG_SECTION, "user" ).strip(), 
             parser.get( __DB_CONFIG_SECTION, "password" ).strip() )

def databaseConn( username, password, dbName ):        
    return MySQLdb.connect( host=__DB_CONN_HOST, port=__DB_CONN_PORT,
                            user=username, passwd=password, db=dbName )

def executeSqlScript( dbName, scriptPath, ignoreErrors=False ) :       
    scriptDirPath = os.path.dirname( os.path.realpath( scriptPath ) )
    sourceCmd = "SOURCE %s" % (scriptPath,)
    cmdList = [ __MYSQL_CLIENT_PATH,                
               "--defaults-extra-file=%s" % (__DB_CONFIG_PATH,) , 
               "--database", dbName,
               "--unbuffered" ] 
    if ignoreErrors : 
        cmdList.append( "--force" )
    else:
        cmdList.extend( ["--execute", sourceCmd ] )
    process = Popen( cmdList 
                   , cwd=scriptDirPath
                   , stdout=PIPE 
                   , stderr=(STDOUT if ignoreErrors else PIPE) 
                   , stdin=(PIPE if ignoreErrors else None) )
    stdOut, stdErr = process.communicate( sourceCmd if ignoreErrors else None )
    if stdErr is not None and len(stdErr) > 0 : 
        raise MySqlScriptError( dbName, scriptPath, stdOut, stdErr )
    return stdOut

如果您想测试一下,请添加:

if __name__ == "__main__": 

    ( username, password ) = databaseLoginParms()
    dbName = "ExampleDatabase"

    print "MySQLdb Test"
    print   
    conn = databaseConn( username, password, dbName )
    cursor = conn.cursor()
    cursor.execute( "show tables" )
    print cursor.fetchall()
    cursor.close()
    conn.close()
    print   

    print "-----------------"
    print "Execute Script with ignore errors"
    print   
    scriptPath = "test.sql"
    print executeSqlScript( dbName, scriptPath, 
                            ignoreErrors=True )
    print   

    print "-----------------"
    print "Execute Script WITHOUT ignore errors"                            
    print   
    try : print executeSqlScript( dbName, scriptPath )
    except MySqlScriptError as e :        
        print "dbName: %s" % (e.dbName,)
        print "scriptPath: %s" % (e.scriptPath,)
        print "errorNum: %s" % (str(e.errorNum),)
        print "sqlState: %s" % (str(e.sqlState),)
        print "priorOutput:"        
        print e.priorOutput
        print
        print "errorMsg:"
        print e.errorMsg           
        print
        print e
    print   

为了更好的衡量,这里有一个示例sql脚本可以输入它:

<强> TEST.SQL

show tables;
blow up;
show tables;

所以,现在进行一些讨论。

首先,我将说明如何使用MySQLdb以及此外部脚本执行,同时将信用卡存储在一个可用于两者的共享文件中。

通过在命令行上使用--defaults-extra-file,您可以安全地传递连接参数。

--force与stdin流式传输源命令或--execute在外部运行命令的组合让你决定脚本将如何运行。这是通过忽略错误并继续运行,或在错误发生时立即停止。

结果回归的顺序也将通过--unbuffered保留。没有它,你的stdout和stderr流将在它们的顺序中混乱和未定义,这使得很难弄清楚在将它与输入sql进行比较时哪些有效,哪些无效。

使用Popen cwd=scriptDirPath让你使用相对路径将源命令嵌套在一起。如果你的脚本都在同一个目录中(或相对于它的已知路径),那么你可以引用相对于顶级脚本所在的目录。

最后,我投入了一个异常类,其中包含了您可能想要的所有信息。如果你没有使用ignoreErrors选项,当出现问题并且脚本停止运行该错误时,你的python中会抛出其中一个异常。

答案 8 :(得分:1)

加载mysqldump文件:

for line in open(PATH_TO_FILE).read().split(';\n'):
    cursor.execute(line)

答案 9 :(得分:1)

如其中一条注释中所述,如果您确定每个命令都以分号结尾,则可以执行以下操作:

muliprocessing.Pool

答案 10 :(得分:0)

这是一个代码段,可以导入来自导出的典型pad={}。 (我成功地使用了Sequel Pro的导出。)处理多行查询和评论(.sql)。

  • 注1:我使用了Thomas K的回复中的初始行,但添加了更多。
  • 注意2:对于新手,请将DB_HOST,DB_PASS等替换为您的数据库连接信息。
#

答案 11 :(得分:0)

您可以使用其他数据库驱动程序吗?
如果是,则可以使用MySQL的MySQL Connector/Python驱动程序。

它的cursor.execute method支持通过传递Multi=True一次执行多个SQL语句。

不需要 用分号分隔文件中的SQL语句。

简单示例(主要是从第二个链接复制并粘贴,我只是添加了从文件中读取SQL的内容)

import mysql.connector

file = open('test.sql')
sql = file.read()

cnx = mysql.connector.connect(user='uuu', password='ppp', host='hhh', database='ddd')
cursor = cnx.cursor()

for result in cursor.execute(sql, multi=True):
  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))

cnx.close()

我正在使用它从* .sql文件导入MySQL转储(通过将整个数据库导出到SQL文件在phpMyAdmin中创建)回到数据库。

答案 12 :(得分:0)

如何使用pexpect library?这个想法是,您可以启动一个流程pexpect.spawn(...),然后等待直到该流程的输出包含某个模式process.expect(pattern)

我实际上使用它来连接到mysql客户端并执行一些sql脚本。

正在连接

import pexpect
process = pexpect.spawn("mysql", ["-u", user, "-p"])
process.expect("Enter password")
process.sendline(password)
process.expect("mysql>")

通过这种方式,密码不会硬编码到命令行参数中(消除了安全风险)。

执行,甚至几个 sql脚本

error = False
for script in sql_scripts:
    process.sendline("source {};".format(script))
    index = process.expect(["mysql>", "ERROR"])

    # Error occurred, interrupt
    if index == 1:
        error = True
        break

if not error:
    # commit changes of the scripts
    process.sendline("COMMIT;")
    process.expect("mysql>")

    print "Everything fine"
else:
    # don't commit + print error message
    print "Your scripts have errors"

请注意,您始终调用expect(pattern),并且该名称与之匹配,否则您将收到超时错误。我需要这段代码来执行多个sql脚本,并且仅在没有错误发生时才提交更改,但是它很容易适应仅具有一个脚本的用例。

答案 13 :(得分:0)

下面也是sqlite的示例(它也适用于MySQL)。

示例file.sql

INSERT INTO actors (name)  VALUES  ('Evan Goldberg')   
INSERT INTO actors (name)  VALUES  ('Jay Baruchel')   
INSERT INTO actors (name)  VALUES  ('Ray Downey')

示例app.py

import sqlite3
from sqlite3 import OperationalError

#Connect to sqlite database
conn = sqlite3.connect('./database.db') 
cur  = conn.cursor()

#Create Table if not exist
create_table="""CREATE TABLE actors
( id INTEGER PRIMARY KEY AUTOINCREMENT,
  name VARCHAR NOT NULL);"""
try:
  execute = cur.execute(create_table)
except OperationalError as e:
  print(e)

#Read File line by line and execute sql
with open("file.sql") as f:  
     query = f.readlines() 
     for sql in query:
         execute = cur.execute(sql)
         conn.commit()

#Check if data was submitted
execute = cur.execute('Select * From actors Order By id asc Limit 3')
rows = cur.fetchall()
print(rows)

conn.close()

您应该看到如下结果:

[(1, 'Evan Goldberg'), (2, 'Jay Baruchel'), (3, 'Ray Downey')]

答案 14 :(得分:0)

你可以使用这样的东西-

def write_data(schema_name: str, table_name: str, column_names: str, data: list):
    try:
        data_list_template = ','.join(['%s'] * len(data))
        insert_query = f"insert into {schema_name}.{table_name} ({column_names}) values {data_list_template}"
        db.execute(insert_query, data)
        conn_obj.commit()
    except Exception as e:
        db.execute("rollback")
        raise e