【问题标题】:How can I parse sql file from within Python?如何从 Python 中解析 sql 文件?
【发布时间】:2013-05-27 11:27:26
【问题描述】:

有什么方法可以在 Python 中执行 .sql 文件中的一些 SQL 命令,但不能执行文件中的所有 SQL 命令?假设我有以下.sql 文件:

DROP TABLE IF EXISTS `tableA`;

CREATE TABLE `tableA`(
some_code
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

DROP TABLE IF EXISTS `tableB`;

CREATE TABLE `tableB`(
some_code
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

DROP TABLE IF EXISTS `tableC`;

CREATE TABLE `tableC`(
some_code
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

...to be continued...

在这个文件中,我只想解析和运行与 tableB 相关的命令(即删除和创建 tableB),但不喜欢在 Python 中对其他表执行任何 SQL 命令。我对如何在 Python 中执行 .sql 文件有一些了解,但不知道如何只执行 .sql 文件中的某些特定命令,如上面示例中所述。让我印象深刻的第一件事是使用正则表达式。但经过一番争论后,由于我的正则表达式知识和经验不足,我无法想出正确的正则表达式语法来得到我所期望的。

所以我的问题是,

1)在这里使用正则表达式来获取所需的命令是否正确,如果是,你能告诉我正确的语法来解析它吗?

2)如果正则表达式不是这里最好的方法,还有什么替代解决方案?

3)我在网上找了一些正则表达式测试工具,但都是指定表达式和测试字符串,并突出显示字符串中匹配的数据。我相信如果有一些工具让我首先指定测试字符串,然后手动突出显示字符串中的所需数据,然后反向返回一些适当的语法/表达式,那将是很棒的。如果您知道此类工具(不限于在线工具!如果是 Macintosh 应用程序我也很高兴),请告诉我...

谢谢。

【问题讨论】:

  • 1) 我想说除了正则表达式之外,您还有其他选择,但这并不意味着您不能使用它。 2)解析语句会更安全。 3)任何基于选择创建正则表达式的工具永远不会同时具有适当的准确和泛化水平。

标签: python sql regex


【解决方案1】:

您可以试用sqlparse 库,该库将通过解析 SQL 语句来简化您的工作,并让您能够在 SQL 语句中查询和使用标记。在您的情况下,它可以成为过滤掉包含特定标记(如 tableB)的语句的 goos 基础

【讨论】:

  • 太棒了。我安装了模块并尝试解析我的文件,它按预期工作。谢谢!
【解决方案2】:

虽然正则表达式可能不是正确的工具,但您仍然可以使用它。

>>> statements = """
... DROP TABLE IF EXISTS `tableA`;
...
... CREATE TABLE `tableA`(
... some_code
... ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
...
... DROP TABLE IF EXISTS `tableB`;
...
... CREATE TABLE `tableB`(
... some_code
... ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
...
... DROP TABLE IF EXISTS `tableC`;
...
... CREATE TABLE `tableC`(
... some_code
... ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
... """
>>> regex = r"((?:CREATE|DROP) TABLE (?:IF (?:NOT )?EXISTS )?`tableB`(?:[^;]|(?:'.*?'))*;)"
>>> re.findall(regex, statements, re.I)
['DROP TABLE IF EXISTS `tableB`;', 'CREATE TABLE `tableB`(\nsome_code\n) ENGINE=MyISAM DEFAULT CHARSET=latin1;']
>>>

如果你想知道什么

`(?:[^;]|(?:'.*?'))*`

是for,它只是用来匹配除;之外的任何字符,任意次数,包括无

一个字符串字面量,这意味着它将允许; 匹配像'this is a ;value; for a varchar field' 这样的字符串。

【讨论】:

  • 谢谢。这部分我还是看不懂:(?:'.*?');为什么你必须用'' 引用.*??我认为(?:[^;])*; 对于最后一部分来说已经足够了......我错过了什么?
  • [^;]+ 匹配任何不是; 的字符。但是一条 sql 语句可以有一个包含 1 的值,这将被 sql 引擎接受。所以我将允许'' 中的任何内容,即使是那个分号。例如,请参阅此问题; stackoverflow.com/questions/16672539/… - 我意识到您的示例案例不需要这,但如果您想匹配一般的 SQL,您将需要它。
  • 有些 sql 查询中包含一个或多个分号,但最后没有,因此您可以允许这些查询也可以工作,'.*?' 表示任何字符(甚至分号)为零或更多次匹配,也尽可能少次......对吗?最后一个问题,'' 引用.*? 是否必不可少?为什么(?:.*?) 不起作用?我想知道你什么时候必须在正则表达式模式中引用'',什么时候不需要。谢谢。
  • 你是对的,你想尽可能少地匹配只得到值,两个值之间什么都没有。对于最后一个问题;我没有使用'' 来使正则表达式的功能有所不同。两个引号都不是正则表达式字符,并且只匹配像a 这样的常规字符。我在引号内有.*? 的原因是因为我希望允许匹配“任何内容”的唯一位置是在 SQL 语句的引用值内。例子; INSERT INTO table (id, name) VALUES (1, 'firstname;lastname');
  • 我意识到插入语句不是您问题的一部分。随意简化你认为合适的。
【解决方案3】:

虽然我个人认为您应该使用一些解析库来解析 SQL 的 AST,但查看代码也使这个选项可行:

my_sql_code = '''DROP TABLE...''' #big long string, multiline
statements = my_sql_code.split(';')
statements = [s for s in statements if 'tableB' in s]
for s in statements:
    execute_sql(s)

【讨论】:

  • 谢谢。我个人喜欢采用 melwil 的正则表达式解决方案或 Boud 所述的 sqlparse 模块,因为如果字符串 tableB 包含在 sql 查询中的某处并且需要更多编码,则它不能很好地工作,但我感谢您的回答。
猜你喜欢
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多