【问题标题】:Stored Procedure Parser存储过程解析器
【发布时间】:2013-03-25 21:01:45
【问题描述】:

我正在尝试解析数百个存储过程,以专门获取它们的输出变量“@FirstName”、它们使用哪些表以及它们从“MyTbl.FirstName”中提取哪些字段。我能够很容易地收集变量,但我在收集表名时遇到了麻烦。有人可以帮忙吗?

到目前为止,我已经能够通过使用 StreamReader 解析 SQL 文件并逐行收集信息来提取这些字段中的大部分,例如,如果一行包含输出,那么我知道该行中的第一个文本是很可能是@Variable。

@Address1 varchar(45) output,
@Address2 varchar(45) output,
@City varchar(35) output,
@State varchar(2) output,
@Zip varchar(10) output

从那里我可以将@Variable 存储到字典中,如果任何行包含@Variable 并且还包含“=”,那么我知道我们匹配到它对应的字段。

@Address1 = c.Address,          
@Address2 = c.AddressSecondLine,
@City = c.City,
@State = c.State,
@Zip = c.ZipOrPostalCode

现在我只是在收集表名时遇到问题。我可以轻松地从字段名称中解析表别名,但在将别名与表名匹配时遇到问题。有谁知道这样做的好方法?到目前为止,这是我一直在尝试的:

FROM Table.dbo.SalesStuff ss
LEFT OUTER JOIN Table.dbo.Customer c ON ss.CustNo = c.CustNo
Left JOIN Table.dbo.Vending v on @tmpVin = v.vin

代码:

keyColl = tables.Keys;
foreach (string var in keyColl)
{
    if (line.Contains(" " + var + '\r') || line.Contains(" " + var + " ") || line.Contains(" " + var + ((char)13)) || line.Contains(" " + var + Environment.NewLine))
    {
        tables[var] = line.ToString();
        break;
    }    
}

我认为这将匹配表别名,因为大多数别名都是一个字母,后跟一个换行符,但到目前为止我还没有得到任何表名......有人知道吗?

【问题讨论】:

  • 如果不知道keyCollline 中的实际内容,甚至不知道您在哪里遍历查询行,就很难判断。你的调试器认为你在​​比较什么?
  • 遗憾的是,我认为在每个查询中依赖单字母别名后跟回车是不安全的...

标签: c# sql-server-2005 stored-procedures text-parsing sql-parser


【解决方案1】:

坦率地说,我认为您的解析想法不会走得太远。您正在对如何在每个过程中格式化代码做出非常大胆的假设。我对格式化非常细致,但我无法保证您在这么多程序中所依赖的那种一致性,即使我自己编写了它们。

请注意,延迟名称解析可能会让您陷入困境,并且依赖项跟踪在 SQL Server 2005 (see the workarounds I posted for keeping it accurate even in SQL Server 2008) 中肯定远非完美,这里有一些想法(它们也不完美,但它们肯定会减少白发):

  1. 您可以使用目录视图sys.parameters,以比暴力解析更简单的方式获取参数:

     SELECT OBJECT_NAME([object_id]), p.name, t.name
       FROM sys.parameters AS p
       INNER JOIN sys.types AS t
       ON p.system_type_id = t.system_type_id
       WHERE p.is_output = 1;
    
  2. 如果您的所有过程都已重新编译并且您不会遇到延迟名称解析问题,您可以从sys.sql_dependencies 获取表名和列名 - 但是这将包括在 where/join 子句中引用的列,即使它们不在选择列表中:

     SELECT [procedure] = OBJECT_NAME(d.[object_id]),
       [table] = OBJECT_NAME(d.referenced_major_id),
       [column] = c.name
       FROM sys.sql_dependencies AS d
       INNER JOIN sys.columns AS c
       ON c.[object_id] = d.referenced_major_id
       AND c.column_id = d.referenced_minor_id;
    

这里有一个名为is_selected 的专栏,但我没有发现它准确/可靠。

请注意,动态 SQL 中发生的任何事情都会保留在动态 SQL 中 - 因此,如果您的过程使用动态 SQL,则几乎不可能剔除表/列名。

【讨论】:

    【解决方案2】:

    您可以使用regular expressions。例如对于像

    这样的字符串
    FROM Table.dbo.SalesStuff ss
    

    你可以使用

      string pattern = @"\s*FROM\s+Table\.dbo\.(\w+)\s+(\w+)";
      string input = "line from stored proc body here";
      MatchCollection matches = Regex.Matches(input, pattern);
    
      foreach (Match match in matches)
      {
         Console.WriteLine("table name:       {0}", match.Groups[1].Value);
         Console.WriteLine("Alias:            {0}", match.Groups[2].Value);
         Console.WriteLine();
      }
    

    您必须为每种类型的包含表名和别名的字符串定义模式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多