如何识别查询语句中有子查询?

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;

【讨论】:

以上是关于如何识别查询语句中有子查询?的主要内容,如果未能解决你的问题,请参考以下文章

低版本中mysql不支持在limit语句中有子查询

低版本中mysql不支持在limit语句中有子查询

IF 语句中带有子查询的 MySQL SELECT 语句的语法

mysql的子查询中有统计语句 我该如何优化

如何在 Laravel 中使用 LIKE 语句使用查询结果并再次查询

mysql性能分析工具