从任何SQL查询中提取列名和表名字符串

本文关键字:字符串 提取 任何 SQL 查询 | 更新日期: 2023-09-27 18:16:49

假设我有一个这样的SQL查询:

SELECT c_mandant, 
    hist_datum, 
    parkey1, 
    parkey2, 
    funktionscode, 
    ma_parkey, 
    me_parkey , 
    CASE 
        WHEN EXISTS 
            ( 
           SELECT 1 
           FROM   cds_h_gruppe GRP1 
           WHERE  grp1.c_mandant = c_mandant 
           AND    grp1.hist_datum = Add_months(Last_day(Trunc(sysdate)), -1)
           AND    grp1.funktionscode = 'H' 
           AND    grp1.parkey1 = ma_parkey
       ) THEN 1 
        ELSE NULL 
    END ma_me , 
    CASE 
        WHEN EXISTS 
            ( 
           SELECT 1 
           FROM   cds_h_gruppe GRP2 
           WHERE  grp2.c_mandant = c_mandant 
           AND    grp2.hist_datum = Add_months(Last_day(Trunc(sysdate)), -1)
           AND    grp2.funktionscode = 'U' 
           AND    grp2.parkey1 = me_parkey
       ) THEN 1 
        ELSE NULL 
    END me_ma,
    Row_number() OVER (partition BY c_mandant, ma_parkey, me_parkey ORDER BY c_mandant, ma_parkey, me_parkey) anz_ma
FROM     
( 
     SELECT c_mandant, 
     hist_datum, 
     parkey1, 
     parkey2, 
     funktionscode , 
     CASE 
       WHEN funktionscode = 'U' THEN parkey1 
       ELSE parkey2 
     END ma_parkey , 
     CASE 
       WHEN funktionscode = 'U' THEN NULL 
       ELSE parkey1 
     END me_parkey 
     FROM   cds_h_gruppe 
     WHERE  funktionscode IN ('U', 
             'H') 
     AND    hist_datum = Add_months(Last_day(Trunc(sysdate)), -1)

我想从查询中提取表名和列名。结果应该类似于

表:

CDS_H_GRUPPE

列:

CDS_H_GRUPPE.c_mandant    
CDS_H_GRUPPE.funktionscode    
CDS_H_GRUPPE.hist_datum    
CDS_H_GRUPPE.parkey1 
CDS_H_GRUPPE.parkey2

我可以从任何复杂的查询字符串中提取列名和表名,这对Oracle, SQL server或DB2有效吗?

从任何SQL查询中提取列名和表名字符串

不要尝试自己解析它,使用SQL解析器。显然,实体框架曾经有一个,但不再有了。似乎还有更多的,比如这个来自讽刺项目。

有一个商业解析器,你可以花900美元(如果你想公开发布你的代码,就得花1900美元)。

在c#中Lexing partial SQL

我想你可能可以使用SqlParser 'Carbunql'来做。

https://github.com/mk3008/Carbunql

例如,可以通过如下方式获取select子句信息:

using Carbunql;
var sq = new SelectQuery(@"SELECT c_mandant, hist_datum, ...");
Console.WriteLine("columns");
sq.SelectClause!.Items.ForEach(item =>
{
    Console.Write("    Alias : " + item.Alias);
    Console.Write(", Type : " +  item.Value.GetType().Name);  
    if (item.Value is ColumnValue c)
    {
        Console.Write(", Table : " + c.TableAlias);
        Console.Write(", Column : " + c.Column);
    }
    Console.WriteLine();
});

执行结果如下所示:

columns
    Alias : c_mandant, Type : ColumnValue, Table : , Column : c_mandant
    Alias : hist_datum, Type : ColumnValue, Table : , Column : hist_datum
    Alias : parkey1, Type : ColumnValue, Table : , Column : parkey1
    Alias : parkey2, Type : ColumnValue, Table : , Column : parkey2
    Alias : funktionscode, Type : ColumnValue, Table : , Column : funktionscode
    Alias : ma_parkey, Type : ColumnValue, Table : , Column : ma_parkey
    Alias : me_parkey, Type : ColumnValue, Table : , Column : me_parkey
    Alias : ma_me, Type : CaseExpression
    Alias : me_ma, Type : CaseExpression
    Alias : anz_ma, Type : FunctionValue

虽然粗略的信息可以很容易地获得,但需要详细地获取引用的表和列名。

这里有一些需要改进的地方。

·在SQL语句的SELECT子句中指定表名可以从from子句中进行推断,但是显式地声明

会更安全。

例子
select t.c_mandant from CDS_H_GRUPPE as t

·计算公式需要递归分析"c_mandant"被解析为ColumnValue类,因此很容易获得列名,但您需要找出其他类在内部引用的列。

·需要从表别名到表的反向查找ColumnValue类可以获取表的别名,而不是表名。需要从别名到表的反向查找。但是,如果在FROM子句中指定了子查询,则必须递归解析子查询以查找物理表名。