存储过程解析器

时间:2013-04-03 23:43:57

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

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

到目前为止,我已经能够通过使用StreamReader解析SQL文件并逐行收集信息来提取大部分字段,例如,如果一行包含输出,那么我知道第一个文本line最有可能是@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;
    }    
}

我认为这会与表别名相匹配,因为大多数别名是一个字母,然后是换行符,但到目前为止我还没有能够获得任何表名...有没有人有一个想法?

2 个答案:

答案 0 :(得分:6)

坦率地说,我认为你的解析思路不会太远。您对如何在每个过程中格式化代码做出了非常勇敢的假设。我对格式化非常细致,但是我无法保证你在很多程序中依赖的那种一致性,即使我自己也写了这些。

有一点需要注意,延迟名称解析可能会让你陷入困境,并且依赖跟踪在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获取表名和列名 - 但是这将包括在哪里引用的列/ 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;
    
  3. 这里有一个名为is_selected的专栏,但我发现它并不准确/可靠。

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

答案 1 :(得分:0)

您可以使用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();
  }

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