2017-09-01 106 views
1

python模块cx_Oracle要求参数化查询,如果字符串正在传递其中语句。我正在从一个文件中读取查询,这些查询看起来如何从像SQL开发人员这样的IDE执行它们。python正则表达式来提取和替换查询参数 - cx_oracle要求

实例查询

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) 

不幸的是,我在正则表达式上非常糟糕,我一直试图用这个数小时来无济于事。

回答

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'] 

正如在评论中指出,你需要重新设置PARAMS列表要分析每个查询。

+0

谢谢您抽出额外时间解释表达。你的分解和链接演示无疑帮助我理解表达式是如何构建的。 – sikrut

+0

快速跟进问题:在完全定义之前,“replacer”函数如何引用自身?我听说过自我指涉的功能,但从来没有见过这样的功能。 – sikrut

+0

@sikrut:在'Python'中,函数就像其他所有的东西一样是对象,所以你可以添加一个像'params'这样的属性,它本身可以是一个字符串,一个列表甚至另一个函数。 – Jan

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可以采取可召回的替代刺痛。可调用函数必须将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; 

在这里!

+0

感谢您使用此解决方案。有趣的看多个表达版本。为什么要用类而不是函数的功能?我的意思可能更根本,因为我真的只在我的宠物项目中使用过函数。没有太多经验的_self教授_ – sikrut

0

Jan的答案唯一的问题是,它不会产生你想要的“:1”,“:2”等字符串。

像下面这样的东西应该工作:

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 

在模式,(?<=)被称为正回顾后,并确保该参数(在这种情况下=\s*,等号后面是任何数量的空格)出现在匹配的部分之前,但不包含在匹配中(因此它将不包含在params中或在替换中被替换)。

+0

很高兴看到这在循环中完成,以及一个额外的表达。没有意识到他们如此灵活,但也许这也使他们有点难以接受。 – sikrut