这是我的用例:Input是一个表示仲裁复杂性的 Oracle PL / SQL语句的字符串。我们可以假设它是单个语句(不是脚本)。 现在,必须重写此输入字符串的几个位。
E.g。表名需要加前缀,选择列表中不使用列别名的聚合函数应该被赋予默认值:
SELECT SUM(ABS(x.value)),
TO_CHAR(y.ID,'111,111'),
y.some_col
FROM
tableX x,
(SELECT DISTINCT ID
FROM tableZ z
WHERE ID > 10) y
WHERE
...
变为
SELECT SUM(ABS(x.value)) COL1,
TO_CHAR(y.ID,'111,111') COL2,
y.some_col
FROM
pref.tableX x,
(SELECT DISTINCT ID, some_col
FROM pref.tableZ z
WHERE ID > 10) y
WHERE
...
(免责声明:只是为了说明问题,声明没有意义)
由于聚合函数可能是嵌套的,而subSELECT是b_tch,所以我不敢使用正则表达式。好吧,实际上我做了并取得了80%的成功,但我确实需要剩余的20%。
我认为正确的方法是使用语法和解析器。 我摆弄了c ++ ANTLR2(虽然我对语法和解析并不太了解)。我没有看到获取SQL位的简单方法:
list<string> *ssel = theAST.getSubSelectList(); // fantasy land
有人可能会就“解析专业人士”如何解决这个问题提供一些指示吗? 编辑:我正在使用 Oracle 9i 。
答案 0 :(得分:2)
也许您可以使用它,它将select语句更改为xml块:
declare
cl clob;
begin
dbms_lob.createtemporary (
cl,
true
);
sys.utl_xml.parsequery (
user,
'select e.deptno from emp e where deptno = 10',
cl
);
dbms_output.put_line (cl);
dbms_lob.freetemporary (cl);
end;
/
<QUERY>
<SELECT>
<SELECT_LIST>
<SELECT_LIST_ITEM>
<COLUMN_REF>
<SCHEMA>MICHAEL</SCHEMA>
<TABLE>EMP</TABLE>
<TABLE_ALIAS>E</TABLE_ALIAS>
<COLUMN_ALIAS>DEPTNO</COLUMN_ALIAS>
<COLUMN>DEPTNO</COLUMN>
</COLUMN_REF>
....
....
....
</QUERY>
见这里:http://forums.oracle.com/forums/thread.jspa?messageID=3693276�
现在你只需要解析这个xml块。
编辑1:
遗憾的是,我并不完全理解OP的需求,但我希望这可以提供帮助(这是询问例如查询select count(*),max(dummy) from dual
的列的'名称'的另一种方式):
set serveroutput on
DECLARE
c NUMBER;
d NUMBER;
col_cnt PLS_INTEGER;
f BOOLEAN;
rec_tab dbms_sql.desc_tab;
col_num NUMBER;
PROCEDURE print_rec(rec in dbms_sql.desc_rec) IS
BEGIN
dbms_output.new_line;
dbms_output.put_line('col_type = ' || rec.col_type);
dbms_output.put_line('col_maxlen = ' || rec.col_max_len);
dbms_output.put_line('col_name = ' || rec.col_name);
dbms_output.put_line('col_name_len = ' || rec.col_name_len);
dbms_output.put_line('col_schema_name= ' || rec.col_schema_name);
dbms_output.put_line('col_schema_name_len= ' || rec.col_schema_name_len);
dbms_output.put_line('col_precision = ' || rec.col_precision);
dbms_output.put_line('col_scale = ' || rec.col_scale);
dbms_output.put('col_null_ok = ');
IF (rec.col_null_ok) THEN
dbms_output.put_line('True');
ELSE
dbms_output.put_line('False');
END IF;
END;
BEGIN
c := dbms_sql.open_cursor;
dbms_sql.parse(c,'select count(*),max(dummy) from dual ',dbms_sql.NATIVE);
dbms_sql.describe_columns(c, col_cnt, rec_tab);
for i in rec_tab.first..rec_tab.last loop
print_rec(rec_tab(i));
end loop;
dbms_sql.close_cursor(c);
END;
/
(有关详情,请参阅此处:http://www.psoug.org/reference/dbms_sql.html)
OP还希望能够在查询中更改表的模式名称。我认为实现这一目标最简单的方法是从user_tables
查询表名,并在sql语句中搜索这些表名并为它们添加前缀或执行'alter session set current_schema = ....'
。
答案 1 :(得分:1)
如果SQL语句字符串的源是其他编码器,您可以简单地坚持要求更改的部分只是用特殊的转义约定标记,例如,写$ TABLE而不是表名,或者$ TABLEPREFIX,其中一个是需要。然后找到需要修补的地方可以通过子字符串搜索和替换来完成。
如果您确实拥有任意SQL字符串并且无法很好地标记它们,则需要按照您的观察以某种方式解析SQL字符串。 XML解决方案当然是一种可能的方式。
另一种方法是使用program transformation system。这样的工具可以解析语言实例的字符串,构建AST,对AST进行分析和转换,然后吐出修改后的字符串。
DMS Software Reengineering Toolkit就是这样一个系统。它有PLSQL前端解析器。它可以使用模式导向的转换来完成您看起来需要的重写。对于涉及选择项目的示例:
domain PLSQL.
rule use_explicit_column(e: expression):select_item -> select_item
"\e" -> "\e \column\(\e\)".
要阅读规则,您需要了解引号内的内容代表我们想要操作的某些计算机语言中的抽象树。 “域PLSQL”短语所说的是“使用PLSQL解析器”来处理引用的字符串内容,这就是它所知道的。 (DMS有很多langauge解析器可供选择)。条款 “表达”和“select_item”是来自感兴趣的语言的语法结构,例如在这种情况下的PLSQL。请参阅PLSQL参考手册中的铁路图。 反斜杠表示转义/元信息,而不是目标语言语法。
规则是什么,转换那些 select_item 的解析元素 仅由表达式 \ n 组成,通过将其转换为由相同表达式 \ e select_item >和相应的列( \ column(\ e))可能基于特定表的选择项列表中的位置。您必须实现列功能,该功能可以从选择项的位置确定相应的名称。在这个例子中,我选择定义列函数来接受感兴趣的表达式作为参数;表达式实际上作为匹配树传递,因此列函数可以通过向上走抽象语法树来确定它在 select_items 列表中的位置。
此规则仅处理选择项。您需要添加更多规则来处理您感兴趣的其他各种情况。
转型系统为您做的是:
虽然编写规则并不总是微不足道,但如果你的问题是必要的 陈述为构成。
XML建议的解决方案是构建此类AST的另一种方法。它没有很好的模式匹配属性,尽管你可以从XSLT中获得很多。我不知道的是XML是否具有完整细节的解析树;如果您想进行任意分析和转换,DMS解析器会根据需要进行设计。