PowerShell中向SQL查询IN语句传递多个值的实现问题
现有代码问题排查
- 直接字符串拼接IN子句的尝试存在两个核心错误:一是拼接的IN值列表缺少外层包裹的
(),生成的SQL语法为keycode IN 'val1','val2',不符合SQL语法要求;二是代码存在变量名混用问题:定义SQL文本时用的变量是$sql,创建SqlCommand时传入的是未赋值的$sqlCmd,连接对象也混用了$TargetConnection和$SourceConnection,运行时会直接抛出变量未定义错误。另外这种直接拼接用户值的写法存在SQL注入风险,值中包含单引号时也会触发语法错误,生产环境禁止使用。 - 参数化查询的尝试存在认知错误:SQL Server原生不支持将单个参数直接展开为IN子句的多值集合,你传入拼接后的单引号包裹字符串时,数据库会将其识别为单个完整字符串值,等价于查询
keycode = 'val1'',''val2',完全无法匹配预期结果。此外你将参数长度设置为4,传入的拼接字符串长度超过4时会被强制截断,进一步导致查询异常。
标准实现方案(全SQL Server版本兼容,无注入风险)
采用动态生成对应数量参数占位符的方式实现,是该场景的通用标准方案:
# 初始化数据库连接 $TargetServer = 'server name' $TargetDatabase = 'db name' $ConnectionString = "Server=$TargetServer;Database=$TargetDatabase;Trusted_Connection=$true;" $TargetConnection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString) $TargetConnection.Open() # 替换为你实际的待查询keycode集合 $keycodeList = $said.Saidkeycode # 为每个值生成独立的参数占位符,例如3个值时生成@SAID0,@SAID1,@SAID2 $paramHolders = for ($i = 0; $i -lt $keycodeList.Count; $i++) { "@SAID$i" } $inSection = $paramHolders -join ',' # 构造SQL语句 $sql = @" SELECT incidentid AS EVENT_LIFECYCLE_VALUE, Priority AS Priority_Code, keycode AS ASSET_KEY, Description AS EVENT_DATA, CreatedDateTime AS [Opened], ClosedDateTime AS [Closed] FROM incident WHERE keycode IN ($inSection) AND Priority IN (1, 2) AND CONVERT(date, CreatedDateTime) <= DATEADD(DAY, -2, CONVERT(date, GETDATE())) AND CreatedDateTime >= DATEADD(DAY, -90, GETDATE()) ORDER BY EVENT_LIFECYCLE_VALUE "@ $command = New-Object System.Data.SqlClient.SqlCommand($sql, $TargetConnection) # 逐个绑定参数值,VarChar长度请匹配你表中keycode字段的实际定义长度 for ($i = 0; $i -lt $keycodeList.Count; $i++) { [void]$command.Parameters.Add("@SAID$i", [Data.SqlDbType]::VarChar, 50) $command.Parameters["@SAID$i"].Value = $keycodeList[$i] } # 读取查询结果 $reader = $command.ExecuteReader() $incidents = @() while ($reader.Read()) { $row = @{} for ($j = 0; $j -lt $reader.FieldCount; $j++) { $row[$reader.GetName($j)] = $reader.GetValue($j) } $incidents += [pscustomobject]$row } $reader.Close() $TargetConnection.Close()
可选简化方案(仅支持SQL Server 2016及以上版本)
如果你的数据库版本支持内置STRING_SPLIT函数,可以不用动态生成参数,直接传入单个分隔字符串即可,代码更简洁:
# SQL语句修改为以下写法 $sql = @" SELECT incidentid AS EVENT_LIFECYCLE_VALUE, Priority AS Priority_Code, keycode AS ASSET_KEY, Description AS EVENT_DATA, CreatedDateTime AS [Opened], ClosedDateTime AS [Closed] FROM incident WHERE keycode IN (SELECT value FROM STRING_SPLIT(@SAID, ',')) AND Priority IN (1, 2) AND CONVERT(date, CreatedDateTime) <= DATEADD(DAY, -2, CONVERT(date, GETDATE())) AND CreatedDateTime >= DATEADD(DAY, -90, GETDATE()) ORDER BY EVENT_LIFECYCLE_VALUE "@ $command = New-Object System.Data.SqlClient.SqlCommand($sql, $TargetConnection) # 直接绑定逗号分隔的原值字符串,不需要额外拼接单引号 [void]$command.Parameters.Add("@SAID", [Data.SqlDbType]::VarChar, -1) $command.Parameters["@SAID"].Value = $said.Saidkeycode -join ',' # 后续结果读取逻辑和上述标准方案完全一致
注意:该方案要求待传入的keycode值本身不能包含英文逗号,否则会出现拆分错误。
如果你使用DataAdapter+DataSet的方式获取结果,在调用Fill($dataSet)方法后,直接访问$dataSet.Tables[0]即可拿到查询返回的结果表,可直接遍历或转换为PowerShell自定义对象使用。
内容的提问来源于stack exchange,提问作者mouseskowitz
相关产品推荐
相关产品推荐

