2016-11-29 74 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

目前沒有,我沒有測試該具體的例子,併發布了這個問題,如果它的工作很好。將反饋 –