2016-11-29 83 views
0

我正在寻找编写一个PowerShell脚本,该脚本将遍历SQL脚本文件的内容,查找包含TABLE提示的行,其中没有WITH语句。添加关键字提示

例子:

  • SELECT * FROM dbo.Table (NOLOCK)
  • SELECT * FROM dbo.Table (INDEX=PK_Table)
  • SELECT * FROM dbo.Table (NOLOCK,INDEX=PK_Table)
  • SELECT * FROM dbo.Table (INDEX=PK_Table,NOLOCK)

我想编写一个PowerShell脚本看起来对于那些提示并在TABLE HINT之前添加WITH

所以它们看起来像下面这样:

  • SELECT * FROM dbo.Table WITH(NOLOCK)
  • SELECT * FROM dbo.Table WITH(INDEX=PK_Table)
  • SELECT * FROM dbo.Table WITH(NOLOCK,INDEX=PK_Table)
  • SELECT * FROM dbo.Table WITH(INDEX=PK_Table,NOLOCK)

到目前为止,我有以下几点:

foreach ($str in Get-Content "d:\script.sql") { 
    if ([regex]::IsMatch($str, "\({0,1}NOLOCK\)|\(,NOLOCK\{0,1}\)","IgnoreCase")) { 
     $strReplace = [regex]::Replace($str, "\({0,1}NOLOCK\)|\(,NOLOCK\{0,1}\)", "WITH $1") 
     write $strReplace 
    } 
} 

但是,它不插入WITH到行中并保持匹配和行。

这也将不得不处理脚本如下所示:

SELECT Table1.*, Table2.* FROM dbo.Table1 Table1 (NOLOCK) 
INNER JOIN dbo.Table2 Table2 (INDEX=PK_Table2, NOLOCK) 
ON (Table2.Id = Table1.Id) 
下面的建议

:我用下面的:

$expr = "(?=\([^)]*(?=(NOLOCK|INDEX=|INDEX\())[^)]*\))(?<!WITH\s+)" 
 

 
if ([regex]::IsMatch($str, "(?=\([^)]*(?=(NOLOCK|INDEX=|INDEX\())[^)]*\))","IgnoreCase")) { 
 
     $replaced = $str -replace $expr,"WITH "

+0

除非SQL语句结构一致,否则字符串解析将会很脆弱。考虑使用T-SQL脚本DOM:https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.transactsql.scriptdom.aspx –

+0

谢谢,我试图转换1000多个程序,可能写入没有WITH关键字,以使它们与新版本的SQL Server保持一致。我找不到任何使用脚本DOM修复非编译程序的示例。 –

+0

另请注意,我们使用Redgate进行SQL格式化和源代码控制,因此它们始终采用相同的格式。 –

回答

1

以下expession会工作,至少在你的输入样本中。没有保证,这将

  • 匹配所有的事情应该
  • 比赛没有任何的东西不应该
  • 没有造成严重破坏你的SQL代码

您需要对所有三种情况进行非常仔细的测试也想到边缘情况。

$sample = " 
SELECT * FROM dbo.Table (NOLOCK) 
SELECT * FROM dbo.Table (INDEX=PK_Table) 
SELECT * FROM dbo.Table (NOLOCK,INDEX=PK_Table) 
SELECT * FROM dbo.Table (INDEX=PK_Table,NOLOCK) 


SELECT * FROM dbo.Table WITH (NOLOCK) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (NOLOCK,INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table,NOLOCK) 
" 

$expr = "(?=\([^()]*(?=\b(?:NOLOCK|INDEX)\b)[^()]*\))(?<!WITH\s+)" 

$sample -replace $expr,"WITH " 

结果:

 
SELECT * FROM dbo.Table WITH (NOLOCK) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (NOLOCK,INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table,NOLOCK) 


SELECT * FROM dbo.Table WITH (NOLOCK) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (NOLOCK,INDEX=PK_Table) 
SELECT * FROM dbo.Table WITH (INDEX=PK_Table,NOLOCK) 

正则表达式的高级击穿:

(?=\([^()]*(?=\b(?:NOLOCK|INDEX)\b)[^()]*\)) # a position followed by the words "INDEX" or 
               # "NOLOCK" somewhere in parentheses 
               # (add more keywords if you want) 
(?<!WITH\s+)         # the same position must not be preceded by "WITH" 

这两个环视条件下准确地确定与关键字缺少的位置。之后插入它与-replace运算符很简单。

详细击穿:

(?=      # start zero-width look-ahead 
    \(     # "(" 
    [^()]*     # any character except "(" and ")", repeat 
    \b      # a word boundary 
    (?=     # start non-matching group 
    (?:NOLOCK|INDEX)  #  "NOLOCK" or "INDEX" 
)      # end group 
    \b      # a word boundary 
    [^()]*     # any character except "(" and ")", repeat 
    \)      # ")" 
)      # end look-ahead 
(?<!      # start zero-width negative look-behind 
    WITH     # "WITH" 
    \s+     # any number of whitespace 
)      # end look-begind 

话虽这么说,你的脚本可以更优雅写成

(Get-Content -Raw D:\script.sql) -replace $expr,"WITH " 

你也可以将输出重定向到一个新文件

(Get-Content -Raw D:\script.sql) -replace $expr,"WITH " > script_processed.sql 

The Raw parameters ter将文件作为一个大字符串读取,而不是按行读取。在这种情况下,逐行处理将不会有用。

+0

Thanks Tomalak,如果您有**(INDEX(PK_Table) ),NOLOCK)**你会如何加入? –

+0

是什么让你觉得这不包括在内? – Tomalak

+0

目前没有,我没有测试该具体的例子,并发布了这个问题,如果它的工作很好。将反馈 –