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 = 未选择任何行

Oracle“插入”命令的奇怪行为

使用 EclipseLink 在 Oracle 上执行查询时出现奇怪的行为

oracle 一对多数据分页查询筛选

Blender联合布尔修饰符行为奇怪

SQL语句怎么筛选表中某一字段不重复的数据行数?