python正则表达式提取和替换查询参数--cx_oracle要求

时间:2017-09-01 18:32:26

标签: python sql regex cx-oracle

如果在 where 语句中传递字符串,则python模块cx_Oracle要求参数化参数。我正在读取文件中的查询,这些查询看起来就像是如何从像sql developer这样的IDE执行它们。

示例查询

select name, count(1) from employees where status = 'Active' and role= 'Manager' group by name order by 1;

我想编写一些函数,将此查询作为输入,然后将参数作为元组吐出:

query = 'select name, count(1) from employees where status = :1 and role= :2 group by name order by 1;'
parms = ('Active','Manager')

这样我就可以在一个简单的函数中传递这两个来执行查询:

cursor_object.execute(query,parms)

不幸的是,我对正则表达方式感到难以置信,而且我已经在这方面努力了几个小时而无济于事。

3 个答案:

答案 0 :(得分:2)

你走了:

import re

sql = """select name, count(1) from employees where status = 'Active' and role= 'Manager' group by name order by 1;"""

rx = re.compile(r"""\w+\s*=\s*'([^']+)'""")
params = rx.findall(sql)
print(params)
# ['Active', 'Manager']

主要部分是

\w+\s*=\s*'([^']+)'

分解,这说:

\w+\s*    # 1+ word characters, 0+ whitespace characters
=\s*      # =, 0+ whitespace characters
'([^']+)' # '(...)' -> group 1

a demo on regex101.com

<小时/> 要同时使用查询和参数,您可以编写一个小函数:

import re

sql = """select name, count(1) from employees where status = 'Active' and role= 'Manager' group by name order by 1;"""

rx = re.compile(r"""(\w+\s*=\s*)'([^']+)'""")

def replacer(match):
    replacer.params.append(match.group(2))
    return '{}:{}'.format(match.group(1), len(replacer.params))

replacer.params = list()
query = rx.sub(replacer, sql)
params = replacer.params

print(query)
print(params)
# select name, count(1) from employees where status = :1 and role= :2 group by name order by 1;
# ['Active', 'Manager']

如评论中所述,您需要为要分析的每个查询重置参数列表。

答案 1 :(得分:1)

快速而肮脏的解决方案是编写与引用字符串匹配的RegEx。你可以这样开始:

import re
import textwrap

query = textwrap.dedent("""\
select name, count(1)
from employees
where status = 'Active' and role= 'Manager'
group by name order by 1;""")

sub_var = re.compile(r"'[^']+'").sub

print(sub_var("VAR", query))
# select name, count(1)
# from employees
# where status = VAR and role= VAR
# group by name order by 1;

但是,在这里你需要替换一个为每次匹配增加自身的值。

要做到这一点,你需要一个功能。请记住,re.sub可以将可调用作为替换刺。 callable必须将MatchObject作为参数并返回替换。

在这里,我更喜欢使用可调用的类:

class CountVar(object):
    def __init__(self):
        self.count = 0

    def __call__(self, mo):
        self.count += 1
        return ":{0}".format(self.count)


print(sub_var(CountVar(), query))
# select name, count(1)
# from employees
# where status = :1 and role= :2
# group by name order by 1;

这是!

答案 2 :(得分:0)

Jan的答案唯一的问题是它不会生成带有&#34;:1&#34;,&#34;:2&#34;等所需的字符串。

以下内容应该有效:

import re
i=1
pattern = r"(?<==\s*)'\w+'"
params = []
while True:
    match = re.find( pattern, cmd )
    if match is None: break
    params.append(match.group())
    cmd = re.sub( pattern, ":" + str(i), 1 )
    i += 1

在模式中,(?<=)被称为正向lookbehind并确保参数(在这种情况下为=\s*,等于后跟任意数量的空格)出现在匹配的部分之前,但它不包含在匹配中(因此它不会包含在params中或在替换中被替换)。