Oracle sql 终止符筛选(+奇怪的行为)
Posted
技术标签:
【中文标题】Oracle sql 终止符筛选(+奇怪的行为)【英文标题】:Oracle sql terminator screening (+strange behavior) 【发布时间】:2015-08-09 18:03:36 【问题描述】:我有一个问题,说这个
SELECT to_char(regexp_substr(q'select * from dual minus select * from dual; select * from dual minus select * from dual;'
, '[^;]+', 1, LEVEL)) FROM dual
CONNECT BY to_char(regexp_substr(q'select * from dual minus select * from dual; select * from dual minus select * from dual;', '[^;]+', 1, LEVEL)) IS NOT NULL;
它工作正常 - 拆分我的行
select * from dual 减号 select * from dual;从双重选择 * 减去 select * from dual;
一分为二
从双重减号中选择* 从双重中选择* 从双重选择 * 减去 select * from dual
在我添加一些线刹车之前一切都很好,就像这样
SELECT to_char(regexp_substr(q'select * from dual minus select * from dual;
select * from dual minus select * from dual;'
, '[^;]+', 1, LEVEL)) FROM dual
CONNECT BY to_char(regexp_substr(q'select * from dual minus select * from dual;
select * from dual minus select * from dual;', '[^;]+', 1, LEVEL)) IS NOT NULL;
这里变成了地狱:sql 将字符串中的;
视为查询的实际结束,ORA-01756 和其他东西......
如果我在;
之后添加一个随机符号,一切都很好,这样
SELECT to_char(regexp_substr(q'select * from dual minus select * from dual;%
select * from dual minus select * from dual;'
, '[^;]+', 1, LEVEL)) FROM dual
CONNECT BY to_char(regexp_substr(q'select * from dual minus select * from dual;%
select * from dual minus select * from dual;', '[^;]+', 1, LEVEL)) IS NOT NULL;
请解释此行为并提出解决方法。
UPD:在不同的 IDE(SQL 开发人员而不是 PL/SQL 开发人员)中进行了尝试。没有错误。也许这完全与编码有关... UPD2:SQLPlus 的工作方式与 PL/SQL 开发人员在这种情况下的工作方式相同。 SQL 开发人员似乎有点“聪明”。不过,不知道为什么。
【问题讨论】:
第二种情况的输出是什么?d有问题? @vks 是的。它没有输出,只给出一组以 ORA-01756 开头的异常,因为 oracle 将此查询视为 3 个未完成的查询。 不确定您从哪里获得编码。听起来这两个 IDE 解析语句的方式有所不同; PL/SQL Developer 似乎以不同的方式选择了行尾的语句分隔符。因为分隔符是客户端的东西,而不是 SQL 语句本身的一部分,所以这是意料之中的——不同的客户端处理它的方式略有不同也就不足为奇了。不同的 PL/SQL Developer 窗口的行为也可能不同。 您只是想解决这个特定问题,还是在寻找更通用的 SQL 语句拆分解决方案?如果要处理所有语句,则需要担心 cmets、替代引用机制字符串、plsql_declarations(并非所有分号都拆分字符串)等。 @JonHeller 我需要用于拆分 sql 的通用解决方案。如果有任何方法可以筛选字符串并阻止 db 解析它们 - 这也将解决您提到的所有问题。 【参考方案1】:试试:
SELECT to_char(regexp_substr(q'select * from dual minus select * from dual;
select * from dual minus select * from dual;'
, '[^;[:cntrl:]]+', 1, LEVEL)) FROM dual
CONNECT BY to_char(regexp_substr(q'select * from dual minus select * from dual;
select * from dual minus select * from dual;', '[^;[:cntrl:]]+', 1, LEVEL)) IS NOT NULL;
输出:
select * from dual minus select * from dual
select * from dual minus select * from dual
原因是 regexp_substr 查找您指定的下一个模式,在您的情况下最初是 [^;]+ 并且此模式会找到 ;下一个字符将是换行符。如果您想分解这样的行,简单的解决方案是通过 [:cntrl:]
在正则表达式搜索中排除控制字符【讨论】:
明智之举,我会考虑将其添加到代码中导致我遇到这个问题。但仍然 - PL/SQL 开发人员和 SQLPlus 不会“吃”这个,而 SQL Developer 会。【参考方案2】:我创建了开源项目plsql_lexer 来解决这些问题。
对于复杂的 SQL 语句,拆分可能会很棘手。拆分报表后,您可能想知道如何处理它们,以及如何报告它们。过程 STATEMENT_CLASSIFIER.CLASSIFY 和 STATEMENT_FEEDBACK.GET_FEEDBACK_MESSAGE 可以帮助完成这项任务。
示例代码
这里有一些示例,从您的示例开始,并添加了一些其他案例。每个示例将字符串拆分为两个语句。
declare
procedure print_split_strings(p_statements nclob) is
v_split_statements nclob_table;
begin
v_split_statements := statement_splitter.split(p_statements);
for i in 1 .. v_split_statements.count loop
dbms_output.put_line('Statement '||i||': '||v_split_statements(i));
end loop;
end;
begin
--This is a simple case.
print_split_strings('select * from dual minus select * from dual; select * from dual minus select * from dual;');
--Ignore semicolons in comments.
print_split_strings('select * from dual a;/* a comment ; */ select * from dual b;');
--Ignore semicolons in strings.
print_split_strings(q'select '''' || q'!'!' from dual a;select * from dual b;');
--Ignore semicolons in matching BEGIN/ENDs in PLSQL_DECLARATIONS:
print_split_strings('with function f return number is begin return 1; end; function g return number is begin return 2; end; select f from dual;select 1 from dual;');
end;
/
Statement 1: select * from dual minus select * from dual;
Statement 2: select * from dual minus select * from dual;
Statement 1: select * from dual a;
Statement 2: /* a comment ; */ select * from dual b;
Statement 1: select '''' || q'!'!' from dual a;
Statement 2: select * from dual b;
Statement 1: with function f return number is begin return 1; end; function g return number is begin return 2; end; select f from dual;
Statement 2: select 1 from dual;
【讨论】:
以上是关于Oracle sql 终止符筛选(+奇怪的行为)的主要内容,如果未能解决你的问题,请参考以下文章
获取 SQL 执行计划。 PL/SQL Developer = 奇怪的行为。 SQL*Plus = 未选择任何行