SQL查询中使用的表的列表

有没有办法获取SQL查询中使用的表列表? 示例:我有类似的东西:

SELECT * FROM Table t JOIN OtherTable ON t.id=OtherTable.t_id 

我希望得到

 Table, OtherTable 

谢谢

您可以在查询后立即使用此sql脚本。 它将返回上次执行的查询中使用的表列表:

  SELECT Field1, Field2 FROM Table t JOIN OtherTable ON t.id=OtherTable.t_id ;WITH vwQueryStats AS( SELECT COALESCE(OBJECT_NAME(s2.objectid),'Ad-Hoc') AS ProcName ,execution_count ,s2.objectid ,( SELECT TOP 1 SUBSTRING(s2.TEXT,statement_start_offset / 2+1 ,( ( CASE WHEN statement_end_offset = -1 THEN (LEN(CONVERT(NVARCHAR(MAX),s2.TEXT)) * 2) ELSE statement_end_offset END)- statement_start_offset) / 2+1)) AS sql_statement ,last_execution_time FROM sys.dm_exec_query_stats AS s1 CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS s2 ) SELECT TOP 1 * INTO #lastQueryStats FROM vwQueryStats x WHERE sql_statement NOT like 'WITH vwQueryStats AS%' ORDER BY last_execution_time DESC SELECT TABLE_NAME FROM #lastQueryStats, INFORMATION_SCHEMA.TABLES tab WHERE CHARINDEX( tab.TABLE_NAME, sql_statement) > 0 DROP TABLE #lastQueryStats 

我已经从这篇文章中获取了检索最后执行的查询的查询,并对其进行了一些修改以与您的示例相匹配。

输出将按您的要求输出:

  Table OtherTable 

然后,如果你想让它们以逗号分隔,你可以这样做:

 DECLARE @tableNames VARCHAR(MAX) SELECT @tableNames = COALESCE(@tableNames + ', ', '') + TABLE_NAME FROM #lastQueryStats, INFORMATION_SCHEMA.TABLES tab WHERE CHARINDEX( tab.TABLE_NAME, sql_statement) > 0 SELECT @tableNames 

但是,您应该警惕在“通常”生产或QA环境中同时执行数千个查询时,这可能不起作用,因为在您的第一个查询和从db stats中提取信息的查询之间可以执行另一个查询。

希望能帮助到你

使用C#的一个解决方案是导入Microsoft.SqlServer.TransactSql.ScriptDom (我在C:\Program Files (x86)\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll找到了dll)然后请执行下列操作:

 private List GetTableNamesFromQueryString(string query) { IList errors = new List(); IList queryTokens; List output = new List(16); StringBuilder sb = new StringBuilder(128); TSql120Parser parser = new TSql120Parser(true); TSqlTokenType[] fromTokenTypes = new TSqlTokenType[2] { TSqlTokenType.From, TSqlTokenType.Join }; TSqlTokenType[] identifierTokenTypes = new TSqlTokenType[2] { TSqlTokenType.Identifier, TSqlTokenType.QuotedIdentifier }; using (System.IO.TextReader tReader = new System.IO.StringReader(query)) { queryTokens = parser.GetTokenStream(tReader, out errors); if (errors.Count > 0) { return errors.Select(e=>"Error: " + e.Number + " Line: " + e.Line + " Column: " + e.Column + " Offset: " + e.Offset + " Message: " + e.Message).ToList(); } for (int i = 0; i < queryTokens.Count; i++) { if(fromTokenTypes.Contains(queryTokens[i].TokenType)) { for (int j = i + 1; j < queryTokens.Count; j++) { if (queryTokens[j].TokenType == TSqlTokenType.WhiteSpace) { continue; } else if (identifierTokenTypes.Contains(queryTokens[j].TokenType)) { sb.Clear(); GetQuotedIdentifier(queryTokens[j], sb); //Change Identifiers to QuotedIdentifier (text only) while (j + 2 < queryTokens.Count && queryTokens[j + 1].TokenType == TSqlTokenType.Dot && identifierTokenTypes.Contains(queryTokens[j + 2].TokenType)) { sb.Append(queryTokens[j + 1].Text); GetQuotedIdentifier(queryTokens[j + 2], sb); //Change Identifiers to QuotedIdentifier (text only) j += 2; } output.Add(sb.ToString()); break; //exit the loop } else { break; } //exit the loop if token is not a FROM, a JOIN, or white space. } } } return output.Distinct().OrderBy(tableName => tableName).ToList(); } } private void GetQuotedIdentifier(TSqlParserToken token, StringBuilder sb) { switch(token.TokenType) { case TSqlTokenType.Identifier: sb.Append('[').Append(token.Text).Append(']'); return; case TSqlTokenType.QuotedIdentifier: sb.Append(token.Text); return; default: throw new ArgumentException("Error: expected TokenType of token should be TSqlTokenType.Identifier or TSqlTokenType.QuotedIdentifier"); } } 

在尝试将这个答案付诸实践后,我想出了这个。

下面的代码基于Trisped的答案,但修改为使用省略模式名称的完全限定表名,以及一些清理/优化:

 public class Parser { public static List GetTableNamesFromQueryString(string query) { var output = new List(); var sb = new StringBuilder(); var parser = new TSql120Parser(true); var fromTokenTypes = new[] { TSqlTokenType.From, TSqlTokenType.Join }; var identifierTokenTypes = new[] { TSqlTokenType.Identifier, TSqlTokenType.QuotedIdentifier }; using (System.IO.TextReader tReader = new System.IO.StringReader(query)) { IList errors; var queryTokens = parser.GetTokenStream(tReader, out errors); if (errors.Any()) { return errors .Select(e => string.Format("Error: {0}; Line: {1}; Column: {2}; Offset: {3}; Message: {4};", e.Number, e.Line, e.Column, e.Offset, e.Message)) .ToList(); } for (var i = 0; i < queryTokens.Count; i++) { if (fromTokenTypes.Contains(queryTokens[i].TokenType)) { for (var j = i + 1; j < queryTokens.Count; j++) { if (queryTokens[j].TokenType == TSqlTokenType.WhiteSpace) { continue; } if (identifierTokenTypes.Contains(queryTokens[j].TokenType)) { sb.Clear(); GetQuotedIdentifier(queryTokens[j], sb); while (j + 2 < queryTokens.Count && queryTokens[j + 1].TokenType == TSqlTokenType.Dot && (queryTokens[j + 2].TokenType == TSqlTokenType.Dot || identifierTokenTypes.Contains(queryTokens[j + 2].TokenType))) { sb.Append(queryTokens[j + 1].Text); if (queryTokens[j + 2].TokenType == TSqlTokenType.Dot) { if (queryTokens[j - 1].TokenType == TSqlTokenType.Dot) GetQuotedIdentifier(queryTokens[j + 1], sb); j++; } else { GetQuotedIdentifier(queryTokens[j + 2], sb); j += 2; } } output.Add(sb.ToString()); } break; } } } return output.Distinct().OrderBy(tableName => tableName).ToList(); } } private static void GetQuotedIdentifier(TSqlParserToken token, StringBuilder sb) { switch (token.TokenType) { case TSqlTokenType.Identifier: sb.Append('[').Append(token.Text).Append(']'); break; case TSqlTokenType.QuotedIdentifier: case TSqlTokenType.Dot: sb.Append(token.Text); break; default: throw new ArgumentException("Error: expected TokenType of token should be TSqlTokenType.Dot, TSqlTokenType.Identifier, or TSqlTokenType.QuotedIdentifier"); } } } 

您可以实现此目的的一种hackish方式是明确命名查询中的字段,并使用表名称作为前缀,例如

 SELECT Field1 As "OtherTable.Field1", Field2 As "Table.Field2" FROM Table t JOIN OtherTable ON t.id=OtherTable.t_id 

实质上,您在查询结果中提供自己的元数据。 查询返回后,查看列名称并实现自定义逻辑以拆分表名称。

Trisped的解决方案非常有效。 我修改了一行以确保不区分大小写并修剪括号。

OLD: output.Add(sb.ToString());

NEW: output.Add(sb.ToString().ToLower().Trim(new char[]{'[', ']'}));