私は現在、Pythonを使用して複数のSQLファイルを実行するスクリプトを作成しています。これはスクリプトを自動化するためのものであり、Pythonは、Windows 2008サーバー上にある唯一のツールです。1つのセットで機能するスクリプトがありますが、問題は、他のセットに2つのステートメントがある場合です「;」で区切られたもののここに私のコードです:
import os
import pyodbc
print ("Connecting via ODBC")
conn = pyodbc.connect('DSN=dsn', autocommit=True)
print ("Connected!\n")
inputdir = 'C:\\path'
cursor = conn.cursor()
for script in os.listdir(inputdir):
with open(inputdir+'\\' + script,'r') as inserts:
sqlScript = inserts.readlines()
sql = (" ".join(sqlScript))
cursor.execute(sql)
print (script)
conn.close()
print ('Run Complete!')
したがって、このコードはファイル全体を表示するように機能しますが、「;」の前に1つのステートメントしか実行しません。
どんな助けでも素晴らしいです!
ありがとう。
Pyodbcコネクタ(またはpymysql)のAPIでは、SQL呼び出しで複数のステートメントを使用できません。これはエンジン解析の問題です。 APIは、複数のステートメントが渡され、複数の結果が返されるときに処理されるために、渡されているSQLを完全に理解する必要があります。
以下のようにスクリプトを少し変更すると、ステートメントを個別に個別のコネクタで送信できるようになります。
_import os
import pyodbc
print ("Connecting via ODBC")
conn = pyodbc.connect('DSN=dsn', autocommit=True)
print ("Connected!\n")
inputdir = 'C:\\path'
for script in os.listdir(inputdir):
with open(inputdir+'\\' + script,'r') as inserts:
sqlScript = inserts.readlines()
for statement in sqlScript.split(';'):
with conn.cursor() as cur:
cur.execute(statement)
print(script)
conn.close()
_
with conn.cursor() as cur:
は、各ステートメントのカーソルを閉じ、各呼び出しが完了した後に適切に終了します。
より正しいアプローチは、コメントと引用符付き文字列を解析し、それらの外の;
sのみを考慮することです。そうしないと、ブロックコメントでいくつかのSQLステートメントをコメントアウトした直後にコードが壊れます。
これは私が自分で作成した状態マシンベースの実装です。このコードはおそらく醜く、はるかによく記述できるので、自由に改善してください。 MySQLスタイルの#
-開始コメントは処理しませんが、簡単に追加できます。
def split_sql_expressions(text):
results = []
current = ''
state = None
for c in text:
if state is None: # default state, outside of special entity
current += c
if c in '"\'':
# quoted string
state = c
Elif c == '-':
# probably "--" comment
state = '-'
Elif c == '/':
# probably '/*' comment
state = '/'
Elif c == ';':
# remove it from the statement
current = current[:-1].strip()
# and save current stmt unless empty
if current:
results.append(current)
current = ''
Elif state == '-':
if c != '-':
# not a comment
state = None
current += c
continue
# remove first minus
current = current[:-1]
# comment until end of line
state = '--'
Elif state == '--':
if c == '\n':
# end of comment
# and we do include this newline
current += c
state = None
# else just ignore
Elif state == '/':
if c != '*':
state = None
current += c
continue
# remove starting slash
current = current[:-1]
# multiline comment
state = '/*'
Elif state == '/*':
if c == '*':
# probably end of comment
state = '/**'
Elif state == '/**':
if c == '/':
state = None
else:
# not an end
state = '/*'
Elif state[0] in '"\'':
current += c
if state.endswith('\\'):
# prev was backslash, don't check for ender
# just revert to regular state
state = state[0]
continue
Elif c == '\\':
# don't check next char
state += '\\'
continue
Elif c == state[0]:
# end of quoted string
state = None
else:
raise Exception('Illegal state %s' % state)
if current:
current = current.rstrip(';').strip()
if current:
results.append(current)
return results
次のように使用します。
with open('myfile.sql', 'r') as sqlfile:
for stmt in split_sql_expressions(sqlfile.read()):
cursor.execute(stmt)