从基于动态SQL的oracle查询中提取列和表名

时间:2012-08-22 17:07:45

标签: sql oracle oracle10g

我想要一个方法/代码段从oracle查询中提取列名和相应的表名。查询以及因此列和表名称在运行时更改,并且通常计算一些列名称,这意味着它们包含在函数和别名中。我尝试了使用regexp的不同字符串标记技术,根据经过的输出将其分开,但到目前为止,没有运气! 例如:

select mandate_name, investment_sub_team_name,
fn_sum(REG_INV_CMP_AUM) REG_INV_CMP_AUM, 
fn_sum(NON_REG_INV_CMP_AUM) NON_REG_INV_CMP_AUM
from DM_SAI_VALUATIONS_STEP3
where position_interval_type = 'E'
and position_type = 'T'
group by mandate_name, investment_sub_team_name;

我希望列的输出为:

mandate_name
investment_sub_team_name
fn_sum(REG_INV_CMP_AUM)
fn_sum(NON_REG_INV_CMP_AUM)

上面注意:我希望列具有函数而不是别名

我希望表名的输出为:DM_SAI_VALUATIONS_STEP3,针对我上面列出的所有列

我无法编辑查询,因为它们是xml输出的一部分。所以,我无法改变别名。第二点是从查询中提取表名。请考虑这样一个事实:没有任何东西可以像字符串标记的位置那样硬编码,因为包含列和表的查询会有所不同。我正在寻找一种通用的方法来标记它们。所以,对于我期望的列输出,我只需要表名。它总是只去一个from子句中的一个表,所以提取它不会是一个问题。

预期产出:

Column Name                          Table Name
-----------                          ----------
mandate_name                         DM_SAI_VALUATIONS_STEP3
investment_sub_team_name             DM_SAI_VALUATIONS_STEP3
fn_sum(REG_INV_CMP_AUM)              DM_SAI_VALUATIONS_STEP3
fn_sum(NON_REG_INV_CMP_AUM)          DM_SAI_VALUATIONS_STEP3

非常感谢任何帮助指针。

3 个答案:

答案 0 :(得分:3)

如果没有编写自己的SQL编译器(至少解析器和词法分析器通过语义分析阶段),你实际上无法解决这个问题。这是一项非常重要的练习,特别是如果您想接受任何有效的Oracle SQL查询。 Oracle公司过去常常为SQL VM和PL / SQL VM提供不同的SQL解析器,并且无法使它们保持同步 - 随着支持的SQL语法的改进,这是时间和精力的一项重要投入,以保持解析器的发展。

如果您真的决心沿着这条路走下去,可以先从ANTLR SQL grammars开始。 O'Reilly Flex and Bison book还有一章解析SQL,你可以将其作为起点。当然,您需要修改和扩展语法,以支持查询可能包含的任何SQL功能。然后,您需要构建编译器的语法分析器和语义分析部分,以实现适当的范围解析规则,以便能够确定对特定列的特定引用来自哪个表。重申一下,这是一项非常重要的工作,必须针对数据库的每个新版本进行增强。

如果您可以放松您的要求并对您将要看到的各种查询做出一些假设,那么编写解析器会变得更加容易。如果您可以保证每个查询都完全引用1个表,那么识别特定列的哪个表就更容易了。如果您可以保证每个函数调用最多只能从一个表中的一列作为参数,那么这也会使事情变得更容易 - 否则,如果列是该列,您将需要确定要为表名返回的内容一个函数的结果,它从多个表中作为参数列。

答案 1 :(得分:0)

如果您知道查询字符串的结构不会发生太大变化,您可以执行以下操作:

set serveroutput on
set termout on
clear
declare
  v_str varchar2(500) := 'select mandate_name, investment_sub_team_name,
fn_sum(REG_INV_CMP_AUM) REG_INV_CMP_AUM, 
fn_sum(NON_REG_INV_CMP_AUM) NON_REG_INV_CMP_AUM
from DM_SAI_VALUATIONS_STEP3
where position_interval_type = ''E''
and position_type = ''T''
group by mandate_name, investment_sub_team_name;';
  v_tmp varchar2(500);
  v_cols varchar2(500);
  v_table varchar2(500);
begin
  v_tmp := replace( v_str, 'select ','');
  v_tmp := substr( v_tmp, 1, instr(v_tmp, 'where')-1);

  dbms_output.put_line('original query: '||v_str);  

  v_cols := substr (v_tmp, 1, instr(v_tmp, 'from')-1);
  dbms_output.put_line('column names: '||v_cols);


  v_table := substr(v_tmp, instr(v_tmp, 'from ')+4, 500);
  dbms_output.put_line('table name: '||v_table);
end;

答案 2 :(得分:0)

我也同意这通常是不可能的。但也许解决方案是与XML消息的创建者取得联系,然后就不同的协议达成一致,然后事先完成SELECT语句。同意他发送专栏。

如果这是不可能的,并且您想对如何构建查询做出某些假设,那么您可以使用select作为分隔符在from之后和,之前进行标记化。但据我所知,你不能通过正则表达式子串命令真正做到这一点。我认为你需要编写一些PL / SQL函数。

但仍需谨慎from关键字可能是列选择的某个部分。如果你突然得到这样的查询,你会怎么做:

select 
  something, 
  (select count(*) from othertable) as cnt, 
  andfromthiscolumn xyz 
from mytable

所以我的建议是在组织上对其进行排序,然后尝试编写不可能的代码。