如何使用替换的参数捕获 SQL? (.NET,SqlCommand)

Posted

技术标签:

【中文标题】如何使用替换的参数捕获 SQL? (.NET,SqlCommand)【英文标题】:How to capture SQL with parameters substituted in? (.NET, SqlCommand) 【发布时间】:2010-05-19 17:43:19 【问题描述】:

是否有一种简单的方法可以在参数替换后返回完整的 SQL 语句?即,我想保留该程序运行的所有 SQL 的日志文件。

或者如果我想这样做,我是否只想摆脱参数,并以老派的方式进行整个查询,在一个大字符串中?

简单示例:我要捕获输出:

SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

.. 来自这段代码:

    Dim subCatSQL As String = "SELECT subcatId FROM EnrollmentSubCategory WHERE catid = @catId"
    Dim connectionString As String = "X"
    Dim conn As New SqlConnection(connectionString)
    If conn.State = ConnectionState.Closed Then
        conn.Open()
    End If
    Dim cmd As New SqlCommand(subCatSQL, conn)
    With cmd
        .Parameters.Add(New SqlParameter("@catId", SqlDbType.Int, 1)) 
    End With

    Console.WriteLine("Before: " + cmd.CommandText)
    cmd.Prepare()
    Console.WriteLine("After: " + cmd.CommandText)

我曾假设 Prepare() 会进行替换,但显然不是。

想法?建议?提前致谢。

【问题讨论】:

【参考方案1】:

不,.Prepare 没有这样做,实际上命令中没有任何内容。参数值永远不会被替换到实际的命令字符串中。它被单独发送到数据库,这有几个原因:

这允许 sqlserver(或其他 dbs)为您的查询缓存查询计划并在下次重用它。当每次向db发送不同的commandtext字符串时,数据库每次都要制定一个计划。 通过将参数发送到划分的db,对sql注入攻击有天然防御。

除非您使用的是非常旧的数据库(sql server 7?),否则.Prepare() 不是必需的,并且实际上不会为您做任何事情。它曾经很有帮助,因为它会在服务器上编译查询,但现在它会自动为您完成。好久没用.Prepare()了。

嗯嗯。看着here 似乎.Prepare() 仍然为您做一些事情:如果任何值大于参数定义的长度, .Prepare() 会截断该值,以便在执行时不会收到错误并且查询成功。很酷。

【讨论】:

感谢所有答案 - 我选择这个作为答案,因为它最好地解释了推理。谢谢大家!【参考方案2】:

我通常在我的项目中添加一个实用函数,给定一个 DbCommand 对象(OracleCommand、SqlCommand 等的父类)将记录查询和参数值。它可能看起来像这样:

Public Shared Sub LogQuery(ByRef cmd As DbCommand)
    If cmd.CommandText Is Nothing Or cmd.CommandText.Length = 0 Then
        Return
    End If

    Dim logFile As CLog = New CLog("sql.log")
    Dim msg As New StringBuilder

    msg.AppendLine("*** Query ***")
    msg.AppendLine(cmd.CommandText)
    msg.AppendLine("*** End Query ***")
    msg.AppendLine("*** Parameters ***")

    For Each p As DbParameter In cmd.Parameters
        msg.AppendLine(String.Format("0: 1", p.ParameterName, p.Value.ToString()))
    Next

    msg.AppendLine("*** End Parameters ***")

    logFile.WriteLog(msg.ToString() & System.Environment.NewLine)
End Sub

在我写这篇文章的时候,我突然想到,你可以做一点 String.Replace()ing 来将参数值替换到查询中,然后记录它,而不是单独记录参数值:

For Each p As DbParameter In cmd.Parameters
    msg = msg.Replace(p.ParameterName, p.Value.ToString())
Next

【讨论】:

我想我也会这样做——它实际上实现了我一直在寻找的东西。【参考方案3】:

参数不被替换。 sql原样,参数本身作为参数传递给sp_executesql系统存储过程

【讨论】:

【参考方案4】:

发送到 SQL Server 的不是用值替换的变量,而是一个准备好的语句,这样可以重复使用

在 SQL Server 中打开探查器,您将看到正在运行的不是 SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

【讨论】:

【参考方案5】:

这是recently asked here。正如其他答案所指出的,参数是带外发送的。

如果你想在服务器上进行捕获,Profiler 可能是你想要使用的,你可以create a trace and use it as a startup job。

【讨论】:

【参考方案6】:

您可以围绕System.Data.SqlClient Provider 构建一个包装器(*例如,在配置文件中注册为... * providerName="System.Data.SqlClient" 的提供程序。本质上是一个拦截代理,您可以访问通过 Provider 传递的所有信息,并且可以抽取您需要的内容,聚合和/或丰富它并将其发送到日志。这有点高级,但打开了捕获整个信息范围的大门,并且可以作为单独的关注层插入/替换/删除。

【讨论】:

【参考方案7】:

试试 CommandAsSql NuGet 包

【讨论】:

以上是关于如何使用替换的参数捕获 SQL? (.NET,SqlCommand)的主要内容,如果未能解决你的问题,请参考以下文章

防止SQL注入

如何使用 .NET 执行多个 Oracle SQL 语句

SQL查找和替换正则表达式捕获组限制?

如何使用 R 在 SQL 查询中执行参数替换? [复制]

正则表达式替换重复捕获

java 替换文本中的所有指定的字符串,比如#*替换为sq, #**替换为we, 我使用了replaceAll进行的替换