执行SQL文件,其中多个语句以";"分隔 使用pyodbc

Nin*_*y12 5 python pyodbc netezza

我目前正在编写一个脚本来使用Python运行多个SQL文件,在您提到替代方法之前有一些背景知识; 这是自动化脚本,Python是我在Windows 2008服务器上使用的唯一工具.我有一个适用于一组的脚本,但问题是当另一组有两个语句而不是一个由';'分隔时 这是我的代码:

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!')
Run Code Online (Sandbox Code Playgroud)

所以这段代码可以显示整个文件,但它只在";"之前执行一条语句.

任何帮助都会很棒!

谢谢.

Mik*_*ana 9

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()
Run Code Online (Sandbox Code Playgroud)

with conn.cursor() as cur:打开关闭游标为每个语句,适当地退出每次通话结束后.

  • 示例中的次要问题-可能您应该执行`inserts.read()`而不是`inserts.readlines()`,因为`readlines`返回不能直接被'split'的字符串列表。 (2认同)
  • **重要**:如果你在 `/* ... */` 注释中有 `;`,这个例子会崩溃,比如注释掉的语句。更稳定的方法是解析注释和字符串文字,并仅在这些实体之外用分号分割。 (2认同)

Mar*_*oft 6

更正确的做法是解析注释和带引号的字符串,只考虑;它们之外的 s。否则你的代码会在你用块注释注释掉几个 SQL 语句后立即被破坏。

这是我为自己制作的基于状态机的实现 - 这段代码可能很丑,可以写得更好,所以请随时通过编辑我的答案来改进它。它不处理 MySQL 风格的#起始注释,但很容易添加。

def split_sql_expressions(text):
    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:
                    yield 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:
            yield current
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

with open('myfile.sql', 'r') as sqlfile:
    for stmt in split_sql_expressions(sqlfile.read()):
        cursor.execute(stmt)
Run Code Online (Sandbox Code Playgroud)