web-dev-qa-db-ja.com

「;」で区切られた複数のステートメントを含むSQLファイルを実行します。 pyodbcの使用

私は現在、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つのステートメントしか実行しません。

どんな助けでも素晴らしいです!

ありがとう。

7
Ninjaboy12

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:は、各ステートメントのカーソルを閉じ、各呼び出しが完了した後に適切に終了します。

12
Mikuana

より正しいアプローチは、コメントと引用符付き文字列を解析し、それらの外の;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)
4
MarSoft