解析TSQL脚本并返回表和列用法

时间:2017-03-31 14:06:59

标签: sql-server tsql parsing usage-statistics

我们目前正在参与数据迁移项目,我们必须分析数据使用情况,这意味着我们需要确定使用哪些表和列以及需要迁移哪些数据以及哪些数据已过时。 我们不仅有很多基于存储过程的SSRS报告,我们还有数百个(如果不是数千个)用于临时分析的TSQL脚本。手动逐一分析几乎是不可能的。

我找了一种方法来解析这些脚本并返回脚本使用的数据。我设法在EditPad中编写一个从脚本中提取表的宏,但是我没有为列做同样的事情。这里的主要问题是别名,CTE,甚至在脚本中区别列名与其他命令。

与简单的基于正则表达式的宏相比,SQL Server必须清楚地了解脚本使用哪些列 - >执行计划。 我们有什么方法可以将此功能用于我们的目的吗?甚至更好:是否有任何第三方工具可以满足我们的需求?到目前为止,我找不到任何东西。

非常感谢任何建议!

3 个答案:

答案 0 :(得分:2)

从执行计划中解析表格和列可能是可能的,但这样做并不容易。 (如果有人说出来,我会看这篇文章。)第三方工具可能这样做,另外需要注意的事情。根据我的经验,我认为这可能是不可行的,而不是你需要的全部程度。

横向方法:您是否可以安排针对数据库“运行所有内容”,以便您知道所有可能的访问数据的尝试都已被命中?如果是这样的话:

  • 创建数据库的副本。
  • 只包含查询所需的尽可能少的数据。
  • (所以,要么备份/恢复,要么使用SSMS“脚本输出"数据库”
  • 配置安全性,以便所使用的登录可以访问
  • 运行第一个查询。它会失败。确定它需要访问哪些表和列。仅授予对这些表和列的访问权限。
  • 运行下一个查询。重复。
  • 您可以运行批量查询并大量添加表/列
  • 首先启用已知/明显的表格以节省时间
  • 一旦所有查询都能成功运行,因为所有相关的表和列都已启用,您就拥有了最小的设置。

事情是......写了这么多,我看到了很多潜在的警告,边缘情况和陷阱(完全取决于你必须使用的东西 - 视图怎么样?触发?同义词?)我不得不质疑它是否是值得努力。如果你确定你会削减数据库的一半,那就去做吧,但是减少10%可能不值得。 (减少10%,尝试重命名最可疑的表,看看会发生什么。)

答案 1 :(得分:2)

部分答案:

基于this article,可以使用PowerShell(或编译的.Net语言)使用Microsoft.SqlServer.Management.SqlParser为Microsoft工具生成的SQL语句生成解析树

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.SqlParser") | Out-Null
$ParseOptions = New-Object Microsoft.SqlServer.Management.SqlParser.Parser.ParseOptions
$ParseOptions.BatchSeparator = 'GO' 
$ParseOptions.CompatibilityLevel = [Microsoft.SqlServer.Management.SqlParser.Common.DatabaseCompatibilityLevel]::Current
$ParseOptions.IsQuotedIdentifierSet = $true
$ParseOptions.TransactSqlVersion= [Microsoft.SqlServer.Management.SqlParser.Common.TransactSqlVersion]::Current

set-psdebug -strict

#$SQL=get-content $Args[0] |foreach-object{"$_`n"} 

$SQL = "SELECT  c.COLUMN_NAME,
        c.TABLE_NAME, 
        t.TABLE_SCHEMA,
        t.TABLE_TYPE,
        t.TABLE_NAME AS t2
FROM INFORMATION_SCHEMA.TABLES AS t
JOIN INFORMATION_SCHEMA.COLUMNS AS c
ON c.TABLE_NAME = t.TABLE_NAME
AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
AND c.TABLE_CATALOG = t.TABLE_CATALOG
"

$Script=[Microsoft.SqlServer.Management.SqlParser.Parser.Parser]::Parse($SQL, $ParseOptions)

$flags = [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic
$Script.GetType().GetProperty("Script", $flags).GetValue($Script).Xml

我安装了本地版本的SSMS 2016和SQL Server 2014,并且无需安装任何其他功能。

在解释树方面仍然需要做很多工作,但是你可以使用它。

答案 2 :(得分:0)

我的一位同事聪明地想到使用XML查询解析执行计划:

执行计划必须保存为XML,然后在网站上过滤以减少级别的深度/数量,不得大于128:

http://xmltoolbox.appspot.com/

1. Paste the XML
2. Add Column Reference as a filter
3. Format xml
4. Save it again as flatfile

可以在SQL中读取和处理过滤的XML:

DECLARE @xml xml = (
       SELECT CAST(BulkColumn AS XML) FROM OPENROWSET(  
       BULK 'c:\temp\Herkunftsselect_filtered.xml',  
       SINGLE_BLOB) AS ExecPlan 
   );

WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS SP)
SELECT DISTINCT
       'Database' = n.xml.value('./@Database','nvarchar(100)'),
       'Schema' =          n.xml.value('./@Schema','nvarchar(100)'),
       'Tabelle' =         n.xml.value('./@Table','nvarchar(100)'),
       'Alias' =           n.xml.value('./@Alias','nvarchar(100)'),
       'Column' =          n.xml.value('./@Column','nvarchar(100)')
FROM @xml.nodes('/Root/SP:ColumnReference') n(xml)
WHERE n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'Expr%' 
  AND n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'Chk%'
  AND n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'Bitmap%'
  AND n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'IsBaseRow%'
  AND n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'Union%'
  AND n.xml.value('./@Column','nvarchar(100)') NOT LIKE 'Segment%'

ORDER BY 1,2,3,4,5

现在,唯一缺少的部分是如何完全自动化循环脚本文件的过程,生成执行计划,过滤xml并运行查询。我的同事考虑的方法可能是将所有脚本文件合并为一个大文件(循环遍历文件并附加它们),这样手动过程只需要进行一次。