来自powershell的SQL插入失败并带引号
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了来自powershell的SQL插入失败并带引号相关的知识,希望对你有一定的参考价值。
嗨首先感谢你看这篇文章。如果我的格式错误,我是新来的,所以道歉。
我正在将SQL审计中的审计规范数据插入到SQL表中 - 问题是当我从PS运行语句时,当SQL输出遇到'时,它会失败'
CREATE LOGIN [TEST] WITH PASSWORD=N'******', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
'******'因报价而失败。
它失败的列是$row.statement
这是我的发言:
# POWERSHELL SCRIPT TO PULL yesterday's AUDIT DATA FROM SELECTED SQL SERVERS
(Read from DBA..SQLAuditTargets)
#Proof of concept Auto Pull data
#Load SMO
#[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null;
#Load SQL Module
#Import-Module SqlPs | Out-Null
#Add-PSSnapin SqlServerCmdletSnapin100 | Out-Null
#Add-PSSnapin SqlServerProviderSnapin100 | Out-Null
# Set target Server/DB to write data to.
$TargetServerInstance=''
$TargetServerDB='DBA'
#Set CMS server source
$CMSSERVER='SQL1'
function Write-DataTable
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)] [string]$ServerInstance,
[Parameter(Position=1, Mandatory=$true)] [string]$Database,
[Parameter(Position=2, Mandatory=$true)] [string]$TableName,
[Parameter(Position=3, Mandatory=$true)] $Data,
[Parameter(Position=4, Mandatory=$false)] [string]$Username,
[Parameter(Position=5, Mandatory=$false)] [string]$Password,
[Parameter(Position=6, Mandatory=$false)] [Int32]$BatchSize=50000,
[Parameter(Position=7, Mandatory=$false)] [Int32]$QueryTimeout=0,
[Parameter(Position=8, Mandatory=$false)] [Int32]$ConnectionTimeout=15
)
$conn=new-object System.Data.SqlClient.SQLConnection
if ($Username)
$ConnectionString = "Server=0;Database=1;User ID=2;Password=3;Trusted_Connection=False;Connect Timeout=4" -f $ServerInstance,$Database,$Username,$Password,$ConnectionTimeout
else
$ConnectionString = "Server=0;Database=1;Integrated Security=True;Connect Timeout=2" -f $ServerInstance,$Database,$ConnectionTimeout
$conn.ConnectionString=$ConnectionString
try
$conn.Open()
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = $tableName
$bulkCopy.BatchSize = $BatchSize
$bulkCopy.BulkCopyTimeout = $QueryTimeOut
$bulkCopy.WriteToServer($Data)
$conn.Close()
catch
$ex = $_.Exception
Write-Error "$ex.Message"
continue
#Write-DataTable
function Out-DataTable
[CmdletBinding()]
param([Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [PSObject[]]$InputObject)
Begin
$dt = new-object Data.datatable
$First = $true
Process
foreach ($object in $InputObject)
$DR = $DT.NewRow()
foreach($property in $object.PsObject.get_properties())
if ($first)
$Col = new-object Data.DataColumn
$Col.ColumnName = $property.Name.ToString()
$DT.Columns.Add($Col)
if ($property.IsArray)
$DR.Item($property.Name) =$property.value | ConvertTo-XML -AS String -NoTypeInformation -Depth 1
else $DR.Item($property.Name) = $property.value
$DT.Rows.Add($DR)
$First = $false
End
Write-Output @(,($dt))
#Out-DataTable
###### Get Dynamic list of servers names to loop through.
#Change LIKE clause to signify Prod/dev etc.
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server="+$CMSServer+";Database=master;Integrated Security=True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "SELECT SQLName FROM DBA..SQLAuditTargets "
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandTimeout = 15
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
#Initialize Array to hold new database objects
$iDatabases = @()
$servers = $DataSet.Tables[0] | % ([string]$_.SQLName).Trim()
#Loop over each SQLName provided (SQLName is column in source table)
foreach ($instance in $servers)
try
"Connecting to $instance" | Write-Host -ForegroundColor green
############################
$DbQuery="DECLARE @auditfile varchar(200) SET @auditfile=(select log_file_path +LEFT(log_file_name, CHARINDEX('_',log_file_name))+'*.sqlaudit' from sys.server_file_audits where log_file_name LIKE '%DBA-SQL-Audit%')
SELECT
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
FROM sys.fn_get_audit_file(@auditfile, NULL, NULL) AS af INNER JOIN
sys.dm_audit_actions AS aa ON af.action_id = aa.action_id INNER JOIN
sys.dm_audit_class_type_map AS ctm ON aa.class_desc = ctm.securable_class_desc
WHERE (af.statement <> '')
AND (aa.class_desc <> 'SERVER')
AND (af.database_name <> 'master')
AND (ctm.class_type_desc = 'DATABASE')
AND (af.action_id IN ('AL', 'DR', 'CR', 'SVPD', 'SVSD', 'SVSR'))
AND (af.object_name NOT IN ('dbo', '', 'telemetry_xevents'))
AND af.Event_Time > '2018-02-27 00:00:28.0000000' and af.Event_Time < '2018-02-28 08:42:28.0000000'
GROUP BY
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
UNION
SELECT
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name , af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
FROM sys.fn_get_audit_file(@auditfile, NULL, NULL) AS af INNER JOIN
sys.dm_audit_actions AS aa ON af.action_id = aa.action_id INNER JOIN
sys.dm_audit_class_type_map AS ctm ON aa.class_desc = ctm.securable_class_desc
WHERE (af.statement <> '')
AND (aa.class_desc <> 'DATABASE')
AND (ctm.class_type_desc = 'SERVER AUDIT')
AND (af.database_name = 'master')
AND (af.object_name NOT IN ('dbo', '', 'telemetry_xevents'))
AND af.Event_Time > '2018-02-26 06:10:01.3531160'
GROUP BY
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name"
write-output $row
#Connect
#$AuditOutput=invoke-sqlcmd -ServerInstance $instance -Query $DbQuery -Querytimeout 30
$AuditOutput=invoke-sqlcmd -ServerInstance $instance -Database 'Master' -Query $DbQuery -Querytimeout 30
############################
######################################################################
# Write Output to Target SQL Server table (SQL1, DBA database)
######################################################################
#Connect to the SQL server and the Database
$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source="+$TargetServerInstance+"; Initial Catalog="+$TargetServerDB+"; Integrated Security=SSPI")
# Open DB Connection
$conn.Open()
##################################
#DATABASES DETAILS INSERT
##################################
#Loop through each row
foreach ($row in $AuditOutput)
#Create SQL Insert Statement with Audit values
$db_insert_stmt = "INSERT INTO [dbo].[SQLAuditStaging]
([event_time]
,[class_desc]
,[action_id]
,[session_id]
,[server_instance_name]
,[database_name]
,[server_principal_name]
,[schema_name]
,[object_name]
,[statement]
,[containing_group_name]
,[covering_action_name])
VALUES ('$($row.event_time)','$($row.class_desc)','$($row.action_id)','$($row.session_id)','$($row.server_instance_name)','$($row.database_name)','$($row.server_principal_name)','$($row.schema_name)','$($row.object_name)','$($row.statement)','$($row.containing_group_name)', '$($row.covering_action_name)')"
# ## Create your command (Services)
$cmddb = $conn.CreateCommand()
$cmddb.CommandText = $db_insert_stmt
# ## Invoke the Insert statement
$cmddb.ExecuteNonQuery()
#Log Details of collection success
# Log collection success to table here
## Close DB Connection
$conn.Close()
catch
"Error on collections with $instance." | Write-Host -ForegroundColor Red
#Error log info collection here
提前致谢....
答案
您需要使用参数化查询。一个是因为你的代码由于引用转义而抛出错误,两个因为字符串连接易受SQL注入攻击。他们并不那么难。
尝试这样的事情:
##################################
#DATABASES DETAILS INSERT
##################################
#Create SQL Insert Statement
$db_insert_stmt = "INSERT INTO [dbo].[SQLAuditStaging] ([event_time], [class_desc], [action_id], [session_id], [server_instance_name], [database_name], [server_principal_name], [schema_name], [object_name], [statement], [containing_group_name], [covering_action_name])
VALUES (@event_time,
@class_desc,
@action_id,
@session_id,
@server_instance_name,
@database_name,
@server_principal_name,
@schema_name,
@object_name,
@statement,
@containing_group_name,
@covering_action_name)"
#Loop through each row
foreach ($row in $AuditOutput)
$cmddb = $conn.CreateCommand()
$cmddb.CommandText = $db_insert_stmt
# Assign parameter values
$cmddb.Parameters.Add('@event_time' ,[System.Data.SqlDbType]::DateTime2).Value = $row.event_time
$cmddb.Parameters.Add('@class_desc' ,[System.Data.SqlDbType]::NVarChar).Value = $row.class_desc
$cmddb.Parameters.Add('@action_id' ,[System.Data.SqlDbType]::Int).Value = $row.action_id
$cmddb.Parameters.Add('@session_id' ,[System.Data.SqlDbType]::Int).Value = $row.session_id
$cmddb.Parameters.Add('@server_instance_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.server_instance_name
$cmddb.Parameters.Add('@database_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.database_name
$cmddb.Parameters.Add('@server_principal_name',[System.Data.SqlDbType]::NVarChar).Value = $row.server_principal_name
$cmddb.Parameters.Add('@schema_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.schema_name
$cmddb.Parameters.Add('@object_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.object_name
$cmddb.Parameters.Add('@statement' ,[System.Data.SqlDbType]::NVarChar).Value = $row.statement
$cmddb.Parameters.Add('@containing_group_name',[System.Data.SqlDbType]::NVarChar).Value = $row.containing_group_name
$cmddb.Parameters.Add('@covering_action_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.covering_action_name
$cmddb.ExecuteNonQuery()
请注意,参数中使用的数据类型应与[dbo].[SQLAuditStaging]
的数据类型匹配。 $row
中列的值也应该是相关的数据类型。我上面猜了一下。使用this chart确定数据类型的匹配方式。
以上是关于来自powershell的SQL插入失败并带引号的主要内容,如果未能解决你的问题,请参考以下文章
当内容在单引号中时,使用 PowerShell 将 SQL 文件/字符串拆分为批处理排除拆分
【SQL】根据一个字段分组求另一个字段的最大值,并带出其他字段