如何识别查询语句中有子查询?
Posted
技术标签:
【中文标题】如何识别查询语句中有子查询?【英文标题】:how to identify query statement has a subquery in it? 【发布时间】:2019-10-16 20:10:46 【问题描述】:为 SQL 培训开发 C# 项目,并根据每个主题的培训提供不同的练习。练习之一是使用子查询编写查询。需要评估用户是否在查询语句中使用/实现了子查询。
问:编写一个 sql 查询,使用子查询显示 Sales.SalesOrderDetail 表中的 SalesOrderID,LineTotal,average LineTotal
Select SalesOrderID,LineTotal [LineTotal],
(Select AVG(LineTotal) from Sales.SalesOrderDetail) as [AverageLineTotal]
from Sales.SalesOrderDetail
[AverageLineTotal]
是一个子查询。
我们可以通过任何方式识别它吗?比如执行计划或SP来识别它在语句中有子查询
有没有办法通过执行计划来识别它??
【问题讨论】:
您对寻找子查询有什么特别的兴趣吗?在某些情况下,您可以使用连接重写子查询。 我正在给用户/学生练习在 sql 语句中使用子查询,并尝试验证他是否使用 C# 代码正确执行。 我认为没有复杂的 SQL 解析器,没有办法将子查询与另一个连接运算符正确区分开来。您可以尝试在 dba.stackexchange.com 中询问,也许有人可以提出一些建议。 【参考方案1】:由于过多的 T-SQL 构造和选项,解析 ad-hoc 脚本本质上是复杂的。话虽如此,针对目标用例的一种稳健方法是使用Microsoft.SqlServer.TransactSql.ScriptDom 解析脚本。
下面是一个示例 PowerShell 脚本,它使用来自 official Microsoft Dacfx NuGet package 的脚本 DOM 程序集,如果需要,可以下载并提取它。
# Add TSqlScript DOM assembly reference, downloading and extracting to the specified location if needed
$scriptDomAssemblyPath = "C:\Temp\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
$scriptDomNuGetUrl = "https://www.nuget.org/api/v2/package/Microsoft.SqlServer.DacFx.x64/150.4384.2"
if(![System.IO.File]::Exists($scriptDomAssemblyPath))
$response = Invoke-WebRequest -Uri $scriptDomNuGetUrl
if ($response.StatusCode -ne 200)
throw "Unable to download Microsoft.SqlServer.TransactSql.ScriptDom NuGet package: $response.StatusCode : $response.StatusDescription"
$tempZipFilePath = "$([System.IO.Path]::GetTempPath())/$([System.IO.Path]::GetRandomFileName()).zip"
[System.IO.File]::WriteAllBytes($tempZipFilePath, $response.Content)
$response.BaseResponse.Dispose()
$tempUnzipFolderPath = "$([System.IO.Path]::GetTempPath())/$([System.IO.Path]::GetRandomFileName())"
Expand-Archive -Path $tempZipFilePath -DestinationPath $tempUnzipFolderPath
$tempZipFilePath | Remove-Item
Move-Item "$tempUnzipFolderPath\lib\net46\Microsoft.SqlServer.TransactSql.ScriptDom.dll" "$scriptDomAssemblyPath"
$tempUnzipFolderPath | Remove-Item -Recurse
Add-Type -Path $scriptDomAssemblyPath
# script to be parsed
$scriptText = @"
Select SalesOrderID,LineTotal [LineTotal],
(Select AVG(LineTotal) from Sales.SalesOrderDetail) as [AverageLineTotal]
from Sales.SalesOrderDetail
"@
#parse script
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser($true)
$parseErrors = New-Object System.Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
$scriptReader = New-Object System.IO.StringReader($scriptText)
$script = $parser.Parse($scriptReader, [ref]$parseErrors)
if($parseErrors.Count -gt 0)
throw "$($parseErrors.Count) parsing errors"
# sanity check for expected SELECT query
if(($script.Batches.Count -ne 1) -or ($script.Batches[0].Statements.Count -ne 1) -or ($script.Batches[0].Statements[0].QueryExpression -eq $null))
throw "script with single SELECT statement expected"
# find scalar subquery expression in select list
$subQueryFound = $false
foreach($selectElement in $script.Batches[0].Statements[0].QueryExpression.SelectElements)
if($selectElement.Expression.ToString() -eq "Microsoft.SqlServer.TransactSql.ScriptDom.ScalarSubquery")
$subQueryFound = $true
break
# show if subquery was used
if($subQueryFound)
Write-Host "A subquery is used"
else
Write-Host "A subquery is not used"
【讨论】:
【参考方案2】:如果这是一个 c# 项目,您可以使用正则表达式解析查询以查找查询是否包含(选择 任何其他文本)。
public static void Main()
var sql = @"Select SalesOrderID,LineTotal [LineTotal],(Select AVG(LineTotal) from Sales.SalesOrderDetail) as [AverageLineTotal] from Sales.SalesOrderDetail";
Console.WriteLine(DoesSqlContainSubquery(sql));
public bool DoesSqlContainSubquery(string sql)
var regexTest = new Regex(@"\( *Select .*\)", RegexOptions.IgnoreCase);
var containsSubquery = regexTest.IsMatch(sql);
return containsSubquery;
【讨论】:
以上是关于如何识别查询语句中有子查询?的主要内容,如果未能解决你的问题,请参考以下文章
IF 语句中带有子查询的 MySQL SELECT 语句的语法