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[]{'[', ']'}));
- 为什么我在使用Microsoft.Bcl时不能在Windows Phone 7.1 MvvmCross项目中使用await关键字 – 无法等待’System.Threading.Tasks.Task?
- 如何防止应用程序在任务管理器中被杀?
- WPF中UserControl中DesignWidth和Width之间的差异
- 使用Microsoft Scripting Control评估’If’表达式(通过c#)
- 什么对拼写检查有好处? Google拼写检查或Hunspell
- 如何在紧凑框架中从字节数组加载程序集
- 同时访问2个线程上的对象
- 如何在不进行转换的情况下指定短的int文字?
- 如何在C#中确定组播数据包的源IP?