Question

I am trying to parse through hundreds of stored procedures to specifically grab their output variables "@FirstName", which tables they use, and which fields they pull from "MyTbl.FirstName". I am able to collect the variables pretty easily but I'm having trouble collecting the table names. Could anyone help?

So far I've been able to pull most of these fields by parsing through the SQL files using the StreamReader and collecting information line by line, for example if a line contains output, then I know the first text in the line is most likely the @Variable.

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

From there I can store the @Variable into a dictionary and if any line contains the @Variable and also contains a '=' then I know we have a match as to which field it corresponds to.

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

Now I'm just having issues gathering the table name. I can easily parse the table alias off the field name but I'm having issues matching the alias with a table name. Does anyone know of a good way to do this? Here's what I've been trying so far:

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

Code:

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;
    }    
}

I thought that this would match the table alias since most aliases are a letter, followed by a line break, but so far I haven't been able to get any of the table names... Does anyone have an idea?

Was it helpful?

Solution

Quite frankly I don't think you're going to get very far with your parsing idea. You're making very brave assumptions about how code is going to be formatted in every single procedure. I'm very meticulous about formatting but there's no way I could guarantee the kind of consistency you're depending on across that many procedures, even if I did write them all myself.

With the caveat that deferred name resolution can bite you in the rear and that dependency tracking was certainly far from perfect in SQL Server 2005 (see the workarounds I posted for keeping it accurate even in SQL Server 2008), here are a couple of ideas (and they're not perfect either, but they'll definitely cause less gray hair):

  1. You can get parameters in a much easier way than brute force parsing, by using the catalog view 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. If all of your procedures have been recompiled and you are not subject to deferred name resolution issues, you can get table names and column names from sys.sql_dependencies - however this will include columns that are referenced in where/join clauses even if they are not in the select list:

     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;
    

There is a column here called is_selected but I have not found it to be accurate/reliable.

Note that anything that happens in dynamic SQL stays in dynamic SQL - so if your procedures use dynamic SQL it will be next to impossible to cull out table/column names.

OTHER TIPS

you can use regular expressions. For example for string like

FROM Table.dbo.SalesStuff ss

you can use

  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();
  }

you must define pattern for each type of string containing table name and alias.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top