Ответ 1
for line in open(PATH_TO_FILE):
cursor.execute(line)
Это предполагает, что у вас есть один оператор SQL для каждой строки в вашем файле. В противном случае вам нужно будет написать несколько правил для объединения строк.
Как выполнить sql script, хранящийся в файле *.sql, используя драйвер python MySQLdb. Я пытался
cursor.execute(file(PATH_TO_FILE).read())
но это не работает, потому что cursor.execute может запускать только одну команду sql сразу. Мой sql script содержит несколько операторов sql. Также я пытался
cursor.execute('source %s'%PATH_TO_FILE)
но также без успеха.
for line in open(PATH_TO_FILE):
cursor.execute(line)
Это предполагает, что у вас есть один оператор SQL для каждой строки в вашем файле. В противном случае вам нужно будет написать несколько правил для объединения строк.
Из 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]
Мне также нужно было выполнить файл 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 = ""
Я уверен, что есть возможности для улучшения, но пока он работает довольно хорошо для меня. Надеюсь, кто-то найдет это полезным.
По крайней мере, 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)
Это сработало для меня:
with open('schema.sql') as f:
cursor.execute(f.read().decode('utf-8'), multi=True)
Другим решением, которое позволяет использовать интерпретатор MySQL без разбора, является использование команды os.system
для запуска командной строки MySQL непосредственно внутри python:
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)
Он избегает любой ошибки синтаксического анализа, когда, например, у вас будет строковая переменная с смайликом ;-)
в ней или если вы проверяете ;
как последний символ, если у вас есть комментарии позже, как SELECT * FROM foo_table; # selecting data
Многие из ответов здесь имеют серьезные недостатки...
Во-первых, не пытайтесь разобрать открытый скрипт SQL! Если вы думаете, что это легко сделать, вы не знаете, насколько надежным и сложным может быть SQL. Серьезные сценарии sql, безусловно, включают в себя операторы и определения процедур, занимающие несколько строк. Также обычно явно объявлять и изменять разделители в середине ваших сценариев. Вы также можете вкладывать исходные команды друг в друга. По многим причинам вы хотите запустить скрипт через клиент MySQL и позволить ему справляться с тяжелыми операциями. Попытка изобрести это чревато опасностью и огромной тратой времени. Может быть, если вы являетесь единственным, кто пишет эти сценарии, и вы не пишете ничего сложного, вам это сойдет с рук, но зачем ограничивать себя до такой степени? А как насчет машинно-сгенерированных скриптов или скриптов, написанных другими разработчиками?
Ответ @jdferreira находится на правильном пути, но также имеет проблемы и недостатки. Наиболее важным является то, что дыра в безопасности открывается путем отправки параметров соединения процессу таким образом.
Вот решение/пример для вашего удовольствия копирования и вставки. Мое расширенное обсуждение следующее:
Сначала создайте отдельный файл конфигурации для сохранения вашего имени пользователя и пароля.
дб-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.
Использование cwd=scriptDirPath
позволяет вам cwd=scriptDirPath
исходные команды друг в друга, используя относительные пути. Если все ваши скрипты будут находиться в одном каталоге (или известном пути к нему), то это позволит вам ссылаться на те, в которых находится скрипт верхнего уровня.
Наконец, я добавил класс исключений, в котором содержится вся информация о том, что произошло. Если вы не используете опцию ignoreErrors, одно из этих исключений будет брошено в ваш питон, когда что-то пойдет не так, и скрипт остановится из-за этой ошибки.
Принятый ответ столкнется с проблемами, когда ваш sql script содержит пустые строки, а ваше предложение предложения охватывает несколько строк. Вместо этого, используя следующий подход, вы решите проблему:
f = open(filename, 'r')
query = " ".join(f.readlines())
c.execute(query)
Загрузите файл mysqldump:
for line in open(PATH_TO_FILE).read().split(';\n'):
cursor.execute(line)
Вот фрагмент кода, который импортирует типичный .sql
, который поступает из экспорта. (Я использовал его с экспортом из Sequel Pro успешно.) Сделки с многострочными запросами и комментариями (#
).
import MySQLdb
from configdb import DB_HOST, DB_PASS, DB_USER, DB_DATABASE_NAME
db = MySQLdb.connect(host=DB_HOST, # your host, usually localhost
user=DB_USER, # your username
passwd=DB_PASS, # your password
db=DB_DATABASE_NAME) # name of the data base
cur = db.cursor()
PATH_TO_FILE = "db-testcases.sql"
fullLine = ''
for line in open(PATH_TO_FILE):
tempLine = line.strip()
# Skip empty lines.
# However, it seems "strip" doesn't remove every sort of whitespace.
# So, we also catch the "Query was empty" error below.
if len(tempLine) == 0:
continue
# Skip comments
if tempLine[0] == '#':
continue
fullLine += line
if not ';' in line:
continue
# You can remove this. It for debugging purposes.
print "[line] ", fullLine, "[/line]"
try:
cur.execute(fullLine)
except MySQLdb.OperationalError as e:
if e[1] == 'Query was empty':
continue
raise e
fullLine = ''
db.close()
Вы можете использовать другой драйвер базы данных?
Если да: то, что вы хотите, возможно с драйвером MySQL Connector/Python от MySQL.
Его метод cursor.execute
поддерживает одновременное выполнение нескольких операторов SQL путем передачи Multi=True
.
Расщепление операторы 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()
Я использую это для импорта дампов MySQL (созданных в phpMyAdmin путем экспорта всей базы данных в файл SQL) из файла *.sql обратно в базу данных.
Как насчет использования библиотеки pexpect? Идея состоит в том, что вы можете запустить процесс 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 и фиксации их изменений только в случае отсутствия ошибок, но его легко адаптировать к сценариям использования только с одним сценарием.