将关键字添加到提示

时间:2016-11-29 13:18:32

标签: sql-server sql-server-2008 powershell stored-procedures

我希望编写一个PowerShell脚本,它将遍历SQL脚本文件的内容,查找包含没有WITH语句的TABLE HINTS的行。

示例:

  • 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 "

1 个答案:

答案 0 :(得分:1)

以下尝试将起作用,至少对您的输入样本有效。无法保证

  • 匹配它应该
  • 的所有内容
  • 不匹配任何不应该&#39;
  • 的东西
  • 不会对您的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"

这两个环视条件确定了缺少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

-Raw参数将文件读取为一个大字符串,而不是按行。在这种情况下,逐行处理是没有用的。