当内容在单引号中时,使用 PowerShell 将 SQL 文件/字符串拆分为批处理排除拆分
Posted
技术标签:
【中文标题】当内容在单引号中时,使用 PowerShell 将 SQL 文件/字符串拆分为批处理排除拆分【英文标题】:Splitting SQL file/String into batches using PowerShell exclude split when content is in single quotes 【发布时间】:2022-01-06 17:07:59 【问题描述】:我正在根据字符串 GO 将我的 SQL 文件分成多个批次。 我将***线程How do I split file on batches using powershell作为参考 并注意到此正则表达式不适用于少数情况
如果 go 在单引号中找到,则字符串将被拆分。 (一世 希望避免拆分单引号内的任何文本) 另一种情况是当我使用 go on Declare 语句时 声明@go我不熟悉正则表达式模式。因此,我尝试搜索一些关于正则表达式的在线文档,并提出了在单引号内查找任何内容以及在拆分时如何忽略文件中的@go 的模式。以下是正则表达式
('([\s\S]*?)') - 让我得到单引号中的字符串,但我不知道如何添加这个匹配以排除当前的正则表达式模式 (?(?:\bGO\b) - 当任何非空白字符位于处理下面 sql 文件中 @go 的 GO 字符串之前时,可以避免拆分李>SQL 文件内容:
select * from testTbl; GO
select * from testTbl2;
GO
Declare @go varchar(15) = 'IGo test'
select @go
GO
SELECT 'go', ' go ', 'asdv Igo asdsad',
'
go
'
GO
create table #Temp
(
IdGo int,
GoId Varchar(50)
)
select * from #Temp
drop table #Temp
GO
PowerShell 脚本行:
$batches = ( $scriptData -split "(?:\bGO\b)" ) | % $_ + "`r`nGO"
注意:var $scriptData
中包含 SQL 文件内容。
这是一种正确的方法,或者当字符串用单引号括起来时,我们如何排除拆分?有没有更清洁的方法来做到这一点?
仅供参考:我会更新另一个线程的答案,一旦我能找到解决方案。或者,如果有人认为它是重复的,我很乐意更新另一个线程并删除它。
更新:所需输出:
select * from testTbl;
GO
select * from testTbl2;
GO
Declare @go varchar(15) = 'IGo test' select @go
GO
SELECT 'go', ' go ', 'asdv Igo asdsad','go'
GO
create table #Temp ( IdGo int, GoId Varchar(50) ) select * from #Temp drop table #Temp
GO
【问题讨论】:
可能值得添加您想要的输出,以便让其他人更好地了解您的需求。此外,您希望定位的GO
是否始终大写?
GO 可以是大写/小写。 @SantiagoSquarzon
【参考方案1】:
为了将您的输入稳健地解析成批次,您需要一个能够可靠识别句法元素的语言解析器 - regexes 并不复杂足以对输入的语法进行建模。
在没有 T-SQL 解析器的情况下,[1]您可以可能使用 PowerShell 自己的语言解析器,[System.Management.Automation.Language.Parser]
,鉴于两种语言之间存在高级别的共性,因此它应该能够识别您输入中孤立的、非@
-前缀GO
标记:
警告关于评论支持:
因为 T-SQL 的注释结构与 PowerShell 不同,使用 PowerShell 解析器会为(隔离的)GO
子字符串产生误报在 cmets 内强>.
因此,下面的解决方案使用基于 regex 的 预处理,它删除所有 cmets(额外的工作涉及后处理步骤,可以保留 cmets),但这并不完全可靠,并且依赖于以下假设:
引用字符串中没有类似注释的结构。 块引号 (/* ... */
) 没有嵌套。
(通过使用balancing group definitions 的更复杂的正则表达式,您或许能够克服这一特殊限制。
# Get the file's content and preprocess it by *removing comments*,
# to prevent GO instances inside them from yielding false positives.
# CAVEAT: This isn't fully robust, but may work well enough in practice.
# See the notes above this code snippet.
$fileContent = (Get-Content -Raw t.txt) -replace '(?m)^\s*--.*' -replace '(?s)/\*.*?\*/'
# Parse the file content into an AST (Abstract Syntax Tree),
# as if it were PowerShell code.
$ast = [System.Management.Automation.Language.Parser]::ParseInput($fileContent, [ref] $null, [ref] $null)
# Get all locations - in terms of line and column number - of isolated,
# unquoted GO tokens.
$locations =
$ast.FindAll( $args[0].Extent.Text -eq 'go' , $false) |
Select-Object -ExpandProperty Extent |
Select-Object StartLineNumber, StartColumnNumber -Unique
# Split the file content into batches by the locations of the
# isolated, unquoted GO tokens, resulting in an array of strings
# each representing a batch, stored in $batches.
$thisBatch = ''
$lineNo = $locNdx = 0
[string[]] $batches =
$fileContent -split '\r?\n' | ForEach-Object
if (++$lineNo -eq $locations[$locNdx].StartLineNumber)
$fromCol = 0
do
$thisBatch + $_.Substring($fromCol, $locations[$locNdx].StartColumnNumber - $fromCol + 2 - 1)
$thisBatch = ''
$fromCol = $locations[$locNdx].StartColumnNumber + 2 - 1
while ($locations[++$locNdx].StartLineNumber -eq $lineNo)
if ($fromCol -lt $_.Length)
$thisBatch = $_.Substring($fromCol) + "`n"
else
$thisBatch += "$_`n"
# If the last batch wasn't terminated with a GO, we must add it now.
# Remove + "`nGO" if you don't want to append a terminating GO.
if ($thisBatch.Trim()) $batches += $thisBatch + "`nGO"
# Diagnostic output, to show the resulting batches:
$batches -join "`n-----------------`n"
上述输出,基于您的示例输入:
select * from testTbl; GO
-----------------
select * from testTbl2;
GO
-----------------
Declare @go varchar(15) = 'IGo test'
select @go
GO
-----------------
SELECT 'go', ' go ', 'asdv Igo asdsad',
'
go
'
GO
-----------------
create table #Temp
(
IdGo int,
GoId Varchar(50)
)
select * from #Temp
drop table #Temp
GO
注意:
没有尝试将每个批次压缩成单行表示,但这应该不是问题。
代码还正确处理一行中的多个批次,例如以下示例中的两个完整批次和一个不完整批次:
select * from testTbl0;GO select * from testTbl1 GO Declare @go varchar(15) = 'IGo test'
此外,还包括碰巧没有以GO
终止的最后一批。
[1] 注意:“GO 不是 Transact-SQL 语句;它是 sqlcmd
和 osql
实用程序和 SQL Server Management Studio 代码编辑器识别的命令。” - 见the docs
文档还声明“Transact-SQL 语句不能与 GO 命令占用同一行。”这会使问题中的第一个示例批次在技术上无效,但 Raj(OP)报告说它仍然有效。支持>
【讨论】:
非常感谢。我可以不将每批压缩成单行。我已经用上面的代码进行了测试并得到了预期的结果,除了一种情况,当文件在同一行有类似的东西时(select * from testTbl;GO select * from testTbl2 GO Declare @go varchar(15) = 'IGo test') .找到第一个 GO 语句后处理停止,结果显示为 (select * from testTbl;GO) 并且从未处理文件中的剩余内容 @RajK 根据 MS Docs,语法应该是GO [count] -- (EOL)
你确定 GO SELECT..
是有效的语法吗?
好点,@SantiagoSquarzon。基于此,即使当前问题中的第一个示例输入行也是无效的。致quote the docs:“Transact-SQL 语句不能与 GO 命令占用同一行。”
@mklement0 有代码修复语法错误应该是不可能的,对吧? ;)
@RajK,请查看我的更新,它应该正确处理多批次在线案例。至于 cmets 中的格式:参见***.com/help/formatting - 简而言之:您只能使用不支持换行符的 inline 代码格式 (`...`
),并且您不能 double `
实例 - 使用 \`
嵌入 `
。以上是关于当内容在单引号中时,使用 PowerShell 将 SQL 文件/字符串拆分为批处理排除拆分的主要内容,如果未能解决你的问题,请参考以下文章