【问题标题】:Get the generated SQL statement from a SqlCommand object?从 SqlCommand 对象中获取生成的 SQL 语句?
【发布时间】:2010-09-20 21:08:07
【问题描述】:

我有以下代码:

Using cmd As SqlCommand = Connection.CreateCommand
    cmd.CommandText = "UPDATE someTable SET Value = @Value"
    cmd.CommandText &= " WHERE Id = @Id"
    cmd.Parameters.AddWithValue("@Id", 1234)
    cmd.Parameters.AddWithValue("@Value", "myValue")
    cmd.ExecuteNonQuery
End Using

我想知道是否有任何方法可以将最终的 SQL 语句作为字符串获取,它应该如下所示:

UPDATE someTable SET Value = "myValue" WHERE Id = 1234

如果有人想知道我为什么要这样做:

  • 用于记录(失败)语句
  • 可以将其复制并粘贴到企业管理器以进行测试

【问题讨论】:

标签: c# vb.net ado.net


【解决方案1】:

出于日志记录的目的,恐怕没有更好的方法,只能自己构造字符串:

string query = cmd.CommandText;

foreach (SqlParameter p in cmd.Parameters)
{
    query = query.Replace(p.ParameterName, p.Value.ToString());
}

【讨论】:

  • 如果我这样做,我将不得不区分不同的数据类型。然后我可以一起跳过参数化查询并执行它。
  • 假人:不是真的。如果您执行准备好的语句,您将面临 sql 注入攻击的风险。为答案 +1。
  • 这里有个问题。如果我有“Param”和“differentParam”作为参数,它将使 differentParam 无用,因为它将其替换为“ValueParam”。假设 Param=Value。
  • 该问题不涉及防御性编码技术,因此空引用检查不是答案的一部分。它应该被实施的事实是隐含的,因此我不认为这是一个建设性的评论。
  • 消除@Alok 指出的类似参数名称问题的更好方法可能是使用query = Regex.Replace(query, @"\b" + p.ParameterName + @"\b", p.Value.ToString()); 替换字符串中的参数。这将替换“整个单词”。它可能不是一个通用的解决方案,因为 \b 标记了单词字符和非单词字符之间的位置,所以如果您的参数名称以 @ 开头,您应该使用 p.ParameterName + @"\b" 替换查询字符串中的参数.
【解决方案2】:

虽然不完美,但这是我为 TSQL 敲定的一些东西 - 可以很容易地针对其他口味进行调整...如果没有别的,它将为您提供自己改进的起点 :)

这在数据类型和输出参数等方面做得很好,类似于在 SSMS 中使用“执行存储过程”。我们主要使用 SP,因此“文本”命令不考虑参数等

    public static String ParameterValueForSQL(this SqlParameter sp)
    {
        String retval = "";

        switch (sp.SqlDbType)
        {
            case SqlDbType.Char:
            case SqlDbType.NChar:
            case SqlDbType.NText:
            case SqlDbType.NVarChar:
            case SqlDbType.Text:
            case SqlDbType.Time:
            case SqlDbType.VarChar:
            case SqlDbType.Xml:
            case SqlDbType.Date:
            case SqlDbType.DateTime:
            case SqlDbType.DateTime2:
            case SqlDbType.DateTimeOffset:
                retval = "'" + sp.Value.ToString().Replace("'", "''") + "'";
                break;

            case SqlDbType.Bit:
                retval = (sp.Value.ToBooleanOrDefault(false)) ? "1" : "0";
                break;

            default:
                retval = sp.Value.ToString().Replace("'", "''");
                break;
        }

        return retval;
    }

    public static String CommandAsSql(this SqlCommand sc)
    {
        StringBuilder sql = new StringBuilder();
        Boolean FirstParam = true;

        sql.AppendLine("use " + sc.Connection.Database + ";");
        switch (sc.CommandType)
        {
            case CommandType.StoredProcedure:
                sql.AppendLine("declare @return_value int;");

                foreach (SqlParameter sp in sc.Parameters)
                {
                    if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output))
                    {
                        sql.Append("declare " + sp.ParameterName + "\t" + sp.SqlDbType.ToString() + "\t= ");

                        sql.AppendLine(((sp.Direction == ParameterDirection.Output) ? "null" : sp.ParameterValueForSQL()) + ";");

                    }
                }

                sql.AppendLine("exec [" + sc.CommandText + "]");

                foreach (SqlParameter sp in sc.Parameters)
                {
                    if (sp.Direction != ParameterDirection.ReturnValue)
                    {
                        sql.Append((FirstParam) ? "\t" : "\t, ");

                        if (FirstParam) FirstParam = false;

                        if (sp.Direction == ParameterDirection.Input)
                            sql.AppendLine(sp.ParameterName + " = " + sp.ParameterValueForSQL());
                        else

                            sql.AppendLine(sp.ParameterName + " = " + sp.ParameterName + " output");
                    }
                }
                sql.AppendLine(";");

                sql.AppendLine("select 'Return Value' = convert(varchar, @return_value);");

                foreach (SqlParameter sp in sc.Parameters)
                {
                    if ((sp.Direction == ParameterDirection.InputOutput) || (sp.Direction == ParameterDirection.Output))
                    {
                        sql.AppendLine("select '" + sp.ParameterName + "' = convert(varchar, " + sp.ParameterName + ");");
                    }
                }
                break;
            case CommandType.Text:
                sql.AppendLine(sc.CommandText);
                break;
        }

        return sql.ToString();
    }

这会按照这些思路生成输出...

use dbMyDatabase;
declare @return_value int;
declare @OutTotalRows   BigInt  = null;
exec [spMyStoredProc]
    @InEmployeeID = 1000686
    , @InPageSize = 20
    , @InPage = 1
    , @OutTotalRows = @OutTotalRows output
;
select 'Return Value' = convert(varchar, @return_value);
select '@OutTotalRows' = convert(varchar, @OutTotalRows);

【讨论】:

  • 干得好,实际上是在尝试解决这里的问题,仅凭努力就获得了赞成。
  • 你的“ToBooleanOrDefault(false)”方法是什么?
  • @Benoittr,你可以在这里看到ToBooleanOrDefault的实现:Question #3244850
  • 做了一些小调整,增加了表值参数。这一切都在 GitHub 上和一个 .Net Standard 2.0 Nuget 包github.com/jphellemons/CommandAsSql 谢谢你 Flapper!我可以加你为合作者吗?
  • 值得注意的是,文本命令可以(并且应该)接受参数,而不仅仅是存储过程
【解决方案3】:

你不能,因为它不会生成任何 SQL。

参数化查询(CommandText 中的查询)作为准备好的语句发送到 SQL Server。执行命令时,参数和查询文本将分开处理。任何时候都不会生成完整的 SQL 字符串。

您可以使用 SQL Profiler 查看幕后情况。

【讨论】:

  • SQL 已生成 - 在 Profiler 中查看 - 这是我想要用于记录目的的文本
  • 除了 SQL Profiler(如果我正确理解了一些 MS 评论,则不推荐使用较新的 SQL Server)还可以根据此处的其他答案使用活动监视器
【解决方案4】:

我需要一个类似于字符串转换器的命令来允许更详细的日志记录,所以我写了这个。它将生成在新会话中重新执行命令所需的文本,包括输出参数和结构化参数。它经过了轻微的测试,但需要注意的是。

例子:

SqlCommand cmd = new SqlCommand("GetEntity", con);
cmd.Parameters.AddWithValue("@foobar", 1);
cmd.Parameters.Add(new SqlParameter(){
    ParameterName = "@outParam",
    Direction = ParameterDirection.Output,
    SqlDbType = System.Data.SqlDbType.Int
});
cmd.Parameters.Add(new SqlParameter(){
    Direction = ParameterDirection.ReturnValue
});
cmd.CommandType = CommandType.StoredProcedure;

将产生:

-- BEGIN COMMAND
DECLARE @foobar INT = 1;
DECLARE @outParam INT = NULL;
DECLARE @returnValue INT;
-- END PARAMS
EXEC @returnValue = GetEntity @foobar = @foobar, @outParam = @outParam OUTPUT
-- RESULTS
SELECT 1 as Executed, @returnValue as ReturnValue, @outParam as [@outParam];
-- END COMMAND

实施:

public class SqlCommandDumper
{
    public static string GetCommandText(SqlCommand sqc)
    {
        StringBuilder sbCommandText = new StringBuilder();

        sbCommandText.AppendLine("-- BEGIN COMMAND");

        // params
        for (int i = 0; i < sqc.Parameters.Count; i++)
            logParameterToSqlBatch(sqc.Parameters[i], sbCommandText);
        sbCommandText.AppendLine("-- END PARAMS");

        // command
        if (sqc.CommandType == CommandType.StoredProcedure)
        {
            sbCommandText.Append("EXEC ");

            bool hasReturnValue = false;
            for (int i = 0; i < sqc.Parameters.Count; i++)
            {
                if (sqc.Parameters[i].Direction == ParameterDirection.ReturnValue)
                    hasReturnValue = true;
            }
            if (hasReturnValue)
            {
                sbCommandText.Append("@returnValue = ");
            }

            sbCommandText.Append(sqc.CommandText);

            bool hasPrev = false;
            for (int i = 0; i < sqc.Parameters.Count; i++)
            {
                var cParam = sqc.Parameters[i];
                if (cParam.Direction != ParameterDirection.ReturnValue)
                {
                    if (hasPrev)
                        sbCommandText.Append(",");
                    sbCommandText.Append(" ");

                    sbCommandText.Append(cParam.ParameterName);
                    sbCommandText.Append(" = ");
                    sbCommandText.Append(cParam.ParameterName);

                    if (cParam.Direction.HasFlag(ParameterDirection.Output))
                        sbCommandText.Append(" OUTPUT");

                    hasPrev = true;
                }
            }
        }
        else
        {
            sbCommandText.AppendLine(sqc.CommandText);
        }

        sbCommandText.AppendLine("-- RESULTS");
        sbCommandText.Append("SELECT 1 as Executed");
        for (int i = 0; i < sqc.Parameters.Count; i++)
        {
            var cParam = sqc.Parameters[i];

            if (cParam.Direction == ParameterDirection.ReturnValue)
            {
                sbCommandText.Append(", @returnValue as ReturnValue");
            }
            else if (cParam.Direction.HasFlag(ParameterDirection.Output))
            {
                sbCommandText.Append(", ");
                sbCommandText.Append(cParam.ParameterName);
                sbCommandText.Append(" as [");
                sbCommandText.Append(cParam.ParameterName);
                sbCommandText.Append(']');
            }
        }
        sbCommandText.AppendLine(";");

        sbCommandText.AppendLine("-- END COMMAND");
        return sbCommandText.ToString();
    }

    private static void logParameterToSqlBatch(SqlParameter param, StringBuilder sbCommandText)
    {
        sbCommandText.Append("DECLARE ");
        if (param.Direction == ParameterDirection.ReturnValue)
        {
            sbCommandText.AppendLine("@returnValue INT;");
        }
        else
        {
            sbCommandText.Append(param.ParameterName);

            sbCommandText.Append(' ');
            if (param.SqlDbType != SqlDbType.Structured)
            {
                logParameterType(param, sbCommandText);
                sbCommandText.Append(" = ");
                logQuotedParameterValue(param.Value, sbCommandText);

                sbCommandText.AppendLine(";");
            }
            else
            {
                logStructuredParameter(param, sbCommandText);
            }
        }
    }

    private static void logStructuredParameter(SqlParameter param, StringBuilder sbCommandText)
    {
        sbCommandText.AppendLine(" {List Type};");
        var dataTable = (DataTable)param.Value;

        for (int rowNo = 0; rowNo < dataTable.Rows.Count; rowNo++)
        {
            sbCommandText.Append("INSERT INTO ");
            sbCommandText.Append(param.ParameterName);
            sbCommandText.Append(" VALUES (");

            bool hasPrev = false;
            for (int colNo = 0; colNo < dataTable.Columns.Count; colNo++)
            {
                if (hasPrev)
                {
                    sbCommandText.Append(", ");
                }
                logQuotedParameterValue(dataTable.Rows[rowNo].ItemArray[colNo], sbCommandText);
                hasPrev = true;
            }
            sbCommandText.AppendLine(");");
        }
    }

    const string DATETIME_FORMAT_ROUNDTRIP = "o";
    private static void logQuotedParameterValue(object value, StringBuilder sbCommandText)
    {
        try
        {
            if (value == null)
            {
                sbCommandText.Append("NULL");
            }
            else
            {
                value = unboxNullable(value);

                if (value is string
                    || value is char
                    || value is char[]
                    || value is System.Xml.Linq.XElement
                    || value is System.Xml.Linq.XDocument)
                {
                    sbCommandText.Append("N'");
                    sbCommandText.Append(value.ToString().Replace("'", "''"));
                    sbCommandText.Append('\'');
                }
                else if (value is bool)
                {
                    // True -> 1, False -> 0
                    sbCommandText.Append(Convert.ToInt32(value));
                }
                else if (value is sbyte
                    || value is byte
                    || value is short
                    || value is ushort
                    || value is int
                    || value is uint
                    || value is long
                    || value is ulong
                    || value is float
                    || value is double
                    || value is decimal)
                {
                    sbCommandText.Append(value.ToString());
                }
                else if (value is DateTime)
                {
                    // SQL Server only supports ISO8601 with 3 digit precision on datetime,
                    // datetime2 (>= SQL Server 2008) parses the .net format, and will 
                    // implicitly cast down to datetime.
                    // Alternatively, use the format string "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK"
                    // to match SQL server parsing
                    sbCommandText.Append("CAST('");
                    sbCommandText.Append(((DateTime)value).ToString(DATETIME_FORMAT_ROUNDTRIP));
                    sbCommandText.Append("' as datetime2)");
                }
                else if (value is DateTimeOffset)
                {
                    sbCommandText.Append('\'');
                    sbCommandText.Append(((DateTimeOffset)value).ToString(DATETIME_FORMAT_ROUNDTRIP));
                    sbCommandText.Append('\'');
                }
                else if (value is Guid)
                {
                    sbCommandText.Append('\'');
                    sbCommandText.Append(((Guid)value).ToString());
                    sbCommandText.Append('\'');
                }
                else if (value is byte[])
                {
                    var data = (byte[])value;
                    if (data.Length == 0)
                    {
                        sbCommandText.Append("NULL");
                    }
                    else
                    {
                        sbCommandText.Append("0x");
                        for (int i = 0; i < data.Length; i++)
                        {
                            sbCommandText.Append(data[i].ToString("x"));
                        }
                    }
                }
                else
                {
                    sbCommandText.Append("/* UNKNOWN DATATYPE: ");
                    sbCommandText.Append(value.GetType().ToString());
                    sbCommandText.Append(" *" + "/ N'");
                    sbCommandText.Append(value.ToString());
                    sbCommandText.Append('\'');
                }
            }
        }

        catch (Exception ex)
        {
            sbCommandText.AppendLine("/* Exception occurred while converting parameter: ");
            sbCommandText.AppendLine(ex.ToString());
            sbCommandText.AppendLine("*/");
        }
    }

    private static object unboxNullable(object value)
    {
        var typeOriginal = value.GetType();
        if (typeOriginal.IsGenericType
            && typeOriginal.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            // generic value, unboxing needed
            return typeOriginal.InvokeMember("GetValueOrDefault",
                System.Reflection.BindingFlags.Public |
                System.Reflection.BindingFlags.Instance |
                System.Reflection.BindingFlags.InvokeMethod,
                null, value, null);
        }
        else
        {
            return value;
        }
    }

    private static void logParameterType(SqlParameter param, StringBuilder sbCommandText)
    {
        switch (param.SqlDbType)
        {
            // variable length
            case SqlDbType.Char:
            case SqlDbType.NChar:
            case SqlDbType.Binary:
                {
                    sbCommandText.Append(param.SqlDbType.ToString().ToUpper());
                    sbCommandText.Append('(');
                    sbCommandText.Append(param.Size);
                    sbCommandText.Append(')');
                }
                break;
            case SqlDbType.VarBinary:
            case SqlDbType.Image:
                    {
                            sbCommandText.Append("VARBINARY");
                            sbCommandText.Append("(MAX /* Specified as ");
                            sbCommandText.Append(param.Size);
                            sbCommandText.Append(" */)");
                    }
                    break;
            case SqlDbType.VarChar:
            case SqlDbType.Text:
                    {
                            sbCommandText.Append("VARCHAR");
                            sbCommandText.Append("(MAX /* Specified as ");
                            sbCommandText.Append(param.Size);
                            sbCommandText.Append(" */)");
                    }
                    break;
            case SqlDbType.NVarChar:
            case SqlDbType.NText:
                    {
                            sbCommandText.Append("NVARCHAR");
                            sbCommandText.Append("(MAX /* Specified as ");
                            sbCommandText.Append(param.Size);
                            sbCommandText.Append(" */)");
                    }
                    break;
            // fixed length
            case SqlDbType.Bit:
            case SqlDbType.TinyInt:
            case SqlDbType.SmallInt:
            case SqlDbType.Int:
            case SqlDbType.BigInt:
            case SqlDbType.SmallMoney:
            case SqlDbType.Money:
            case SqlDbType.Decimal:
            case SqlDbType.Real:
            case SqlDbType.Float:
            case SqlDbType.Date:
            case SqlDbType.DateTime:
            case SqlDbType.DateTime2:
            case SqlDbType.DateTimeOffset:
            case SqlDbType.UniqueIdentifier:
                {
                    sbCommandText.Append(param.SqlDbType.ToString().ToUpper());
                }
                break;
            // Unknown
            case SqlDbType.Timestamp:
            default:
                {
                    sbCommandText.Append("/* UNKNOWN DATATYPE: ");
                    sbCommandText.Append(param.SqlDbType.ToString().ToUpper());
                    sbCommandText.Append(" *" + "/ ");
                    sbCommandText.Append(param.SqlDbType.ToString().ToUpper());
                }
                break;
        }
    }
}

【讨论】:

  • 谢谢你,它非常全面! :-)
  • 正是我想要的,谢谢。
  • 我用它作为它的一个版本的起点,它使用 sp_executesql 来处理单个语句中的参数,而不是单独声明变量。这段代码真的处理了所有繁琐的工作,我只需要重新排列这些部分。非常感谢!
  • 这不需要 SQL 字符串文字的“N”前缀吗?否则你可能会得到很多“?”。默默。坏的。 (至少对于 SQL Server 2005 - 没有检查过不太古老的版本。)
  • @PaulGroke,很好的收获。我已更新以包含 N 前缀。
【解决方案5】:

我也有这个问题,一些参数化查询或 sp 会给我一个 SqlException(主要是字符串或二进制数据会被截断),以及难以调试的语句(据我所知,目前没有 sql- SQL Azure 的分析器支持)

我在这里看到了很多反应中的类似代码。我最终将我的解决方案放在一个 Sql-Library 项目中以备将来使用。

生成器在此处可用: https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.Library/SqlGenerator.cs

它同时支持 CommandType.Text 和 CommandType.StoredProcedure

如果您安装了nuget-package,您可以使用以下语句生成它:

SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters);

【讨论】:

  • 还不错,它至少列出了每个参数的值,但实际上仍然没有填写值。至少我可以自己用记事本来做,谢谢!
【解决方案6】:

如果您使用的是 SQL Server,则可以使用 SQL Server Profiler(如果有)来查看实际执行的命令字符串。这对于复制/粘贴测试目的很有用,但恐怕不适用于日志记录。

【讨论】:

    【解决方案7】:

    迟到的答案,我知道,但我也想要这个,所以我可以记录 SQL。以下内容很简短,符合我的需要。

    以下生成的 SQL 您可以在 SSMS 中复制/粘贴(它将参数正确替换为值)。您可以添加更多类型,但这符合我在这种情况下使用的所有类型。

        private static void LogSQL(SqlCommand cmd)
            {
                string query = cmd.CommandText;
    
                foreach (SqlParameter prm in cmd.Parameters)
                {
                    switch (prm.SqlDbType)
                    {
                        case SqlDbType.Bit:
                            int boolToInt = (bool)prm.Value ? 1 : 0;
                            query = query.Replace(prm.ParameterName, string.Format("{0}", (bool)prm.Value ? 1 : 0));
                            break;
                        case SqlDbType.Int:
                            query = query.Replace(prm.ParameterName, string.Format("{0}", prm.Value));
                            break;
                        case SqlDbType.VarChar:
                            query = query.Replace(prm.ParameterName, string.Format("'{0}'", prm.Value));
                            break;
                        default:
                            query = query.Replace(prm.ParameterName, string.Format("'{0}'", prm.Value));
                            break;
                    }
                }
    
                // the following is my how I write to my log - your use will vary
                logger.Debug("{0}", query);
    
                return;
            }
    

    现在我可以在执行 SQL 之前记录它:

    LogSQL(queryCmd)
    queryCmd.ExecuteNonQuery()
    

    【讨论】:

      【解决方案8】:

      Profiler 无疑是您的最佳选择。

      由于涉及到准备 + 执行步骤,您可能需要从分析器复制一组语句。

      【讨论】:

        【解决方案9】:

        我有同样的确切问题,在阅读了这些回复后,我错误地认为无法获得确切的结果查询。我错了。

        解决方案:SQL Server Management Studio 中打开Activity Monitor,将进程部分缩小到您的应用程序在连接字符串中使用的登录用户名、数据库或应用程序名称。当调用 db refresh Activity Monitor。当你看到进程时,右键单击它并View Details

        注意,对于繁忙的数据库,这可能不是一个可行的选择。但是您应该能够使用这些步骤大大缩小结果范围。

        【讨论】:

          【解决方案10】:

          在我的解决方案中使用了Flapper's code 的一部分,它返回整个 SQL 字符串,包括要在 MS SQL SMS 中运行的参数值。

          public string ParameterValueForSQL(SqlParameter sp)
              {
                  string retval = "";
          
                  switch (sp.SqlDbType)
                  {
                      case SqlDbType.Char:
                      case SqlDbType.NChar:
                      case SqlDbType.NText:
                      case SqlDbType.NVarChar:
                      case SqlDbType.Text:
                      case SqlDbType.Time:
                      case SqlDbType.VarChar:
                      case SqlDbType.Xml:
                      case SqlDbType.Date:
                      case SqlDbType.DateTime:
                      case SqlDbType.DateTime2:
                      case SqlDbType.DateTimeOffset:
                          if (sp.Value == DBNull.Value)
                          {
                              retval = "NULL";
                          }
                          else
                          {
                              retval = "'" + sp.Value.ToString().Replace("'", "''") + "'";
                          }
                          break;
          
                      case SqlDbType.Bit:
                          if (sp.Value == DBNull.Value)
                          {
                              retval = "NULL";
                          }
                          else
                          {
                              retval = ((bool)sp.Value == false) ? "0" : "1";
                          }
                          break;
          
                      default:
                          if (sp.Value == DBNull.Value)
                          {
                              retval = "NULL";
                          }
                          else
                          {
                              retval = sp.Value.ToString().Replace("'", "''");
                          }
                          break;
                  }
          
                  return retval;
              }
          
          
              public string CommandAsSql(SqlCommand sc)
              {
                  string sql = sc.CommandText;
          
                  sql = sql.Replace("\r\n", "").Replace("\r", "").Replace("\n", "");
                  sql = System.Text.RegularExpressions.Regex.Replace(sql, @"\s+", " ");
          
                  foreach (SqlParameter sp in sc.Parameters)
                  {
                      string spName = sp.ParameterName;
                      string spValue = ParameterValueForSQL(sp);
                      sql = sql.Replace(spName, spValue);
                  }
          
                  sql = sql.Replace("= NULL", "IS NULL");
                  sql = sql.Replace("!= NULL", "IS NOT NULL");
                  return sql;
              }
          

          【讨论】:

          • 您的“解决方案”不起作用。当您应该使用“”时,您将 \r 和 \n 替换为“”。此外,如果您有超过 9 个参数,则它不起作用,因为替换 '@p1' 会用各种疯狂的结果替换 '@p1' 和 '@p10'。复制参数列表并反转它是我正在做的快速修复。
          • 此外,由于“is null”替换,您的代码不适用于更新命令。
          • 确实 Flapper 的代码不能处理 DBNull,这里有一个基于它的 CommandAsSQL 库的问题:github.com/jphellemons/CommandAsSql/issues/1
          【解决方案11】:

          我的解决方案:

          public static class DbHelper
          {
              public static string ToString(this DbParameterCollection parameters, string sqlQuery)
              {
                  return parameters.Cast<DbParameter>().Aggregate(sqlQuery, (current, p) => current.Replace(p.ParameterName, p.Value.ToString()));
              }
          }
          

          【讨论】:

            【解决方案12】:

            我为我写了这个方法。我使用了Bruno Ratnieks's 代码的一部分。也许它对某人有用。

             public static string getQueryFromCommand(SqlCommand cmd)
                {
                    StringBuilder CommandTxt = new StringBuilder();
                    CommandTxt.Append("DECLARE ");
                    List<string> paramlst = new List<string>();
                    foreach (SqlParameter parms in cmd.Parameters)
                    {
                        paramlst.Add(parms.ParameterName);
                        CommandTxt.Append(parms.ParameterName + " AS ");
                        CommandTxt.Append(parms.SqlDbType.ToString());
                        CommandTxt.Append(",");
                    }
            
                    if (CommandTxt.ToString().Substring(CommandTxt.Length-1, 1) == ",")
                        CommandTxt.Remove(CommandTxt.Length-1, 1);
                    CommandTxt.AppendLine();
                    int rownr = 0;
                    foreach (SqlParameter parms in cmd.Parameters)
                    {
                        string val = String.Empty;
                        if (parms.DbType.Equals(DbType.String) || parms.DbType.Equals(DbType.DateTime))
                            val = "'" + Convert.ToString(parms.Value).Replace(@"\", @"\\").Replace("'", @"\'") + "'";
                        if (parms.DbType.Equals(DbType.Int16) || parms.DbType.Equals(DbType.Int32) || parms.DbType.Equals(DbType.Int64) || parms.DbType.Equals(DbType.Decimal) || parms.DbType.Equals(DbType.Double))
                            val = Convert.ToString(parms.Value);
            
                        CommandTxt.AppendLine();
                        CommandTxt.Append("SET " + paramlst[rownr].ToString() + " = " + val.ToString());
                        rownr += 1;
                    }
                    CommandTxt.AppendLine();
                    CommandTxt.AppendLine();
                    CommandTxt.Append(cmd.CommandText);
                    return CommandTxt.ToString();
                }
            

            【讨论】:

              【解决方案13】:

              如果您的数据库是 Oracle 并且 sql 文本包含名为 :1,:2 ,... 的动态变量,那么您可以使用:

              string query = cmd.CommandText;
              int i = 1;
              foreach (OracleParameter p in cmd.Parameters)
                {
                  query = query.Replace(":"+i.ToString(),((p.Value==null)?"":p.Value.ToString()));
                  i++;
                }
              

              【讨论】:

                【解决方案14】:

                如果只是在结果查询中检查参数的格式,大多数 DBMS 将允许从无到有查询文字。因此:

                Using cmd As SqlCommand = Connection.CreateCommand
                    cmd.CommandText = "SELECT @Value"
                    cmd.Parameters.AddWithValue("@Value", "myValue")
                    Return cmd.ExecuteScalar
                End Using
                

                这样你就可以看到引号是否加倍,等等。

                【讨论】:

                  【解决方案15】:

                  这是我用来将存储过程的参数列表输出到调试控制台的方法:

                  string query = (from SqlParameter p in sqlCmd.Parameters where p != null where p.Value != null select string.Format("Param: {0} = {1},  ", p.ParameterName, p.Value.ToString())).Aggregate(sqlCmd.CommandText, (current, parameter) => current + parameter);
                  Debug.WriteLine(query);
                  

                  这将生成一个与此类似的控制台输出:

                  Customer.prGetCustomerDetails: @Offset = 1,  Param: @Fetch = 10,  Param: @CategoryLevel1ID = 3,  Param: @VehicleLineID = 9,  Param: @SalesCode1 = bce,  
                  

                  我将这段代码直接放在我希望调试的任何过程下方,它类似于 sql profiler 会话,但在 C# 中。

                  【讨论】:

                    【解决方案16】:

                    Kon's answer 的修改版本,因为它仅部分适用于类似的命名参数。使用字符串替换功能的缺点。除此之外,我完全相信他的解决方案。

                    private string GetActualQuery(SqlCommand sqlcmd)
                    {
                        string query = sqlcmd.CommandText;
                        string parameters = "";
                        string[] strArray = System.Text.RegularExpressions.Regex.Split(query, " VALUES ");
                    
                        //Reconstructs the second half of the SQL Command
                        parameters = "(";
                    
                        int count = 0;
                        foreach (SqlParameter p in sqlcmd.Parameters)
                        {
                            if (count == (sqlcmd.Parameters.Count - 1))
                            {
                                parameters += p.Value.ToString();
                            }
                            else
                            {
                                parameters += p.Value.ToString() + ", ";
                            }
                            count++;
                        }
                    
                        parameters += ")";
                    
                        //Returns the string recombined.
                        return strArray[0] + " VALUES " + parameters;
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      这个解决方案现在对我有用。也许它对某人有用。请原谅所有的冗余。

                          Public Shared Function SqlString(ByVal cmd As SqlCommand) As String
                          Dim sbRetVal As New System.Text.StringBuilder()
                          For Each item As SqlParameter In cmd.Parameters
                              Select Case item.DbType
                                  Case DbType.String
                                      sbRetVal.AppendFormat("DECLARE {0} AS VARCHAR(255)", item.ParameterName)
                                      sbRetVal.AppendLine()
                                      sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value)
                                      sbRetVal.AppendLine()
                      
                                  Case DbType.DateTime
                                      sbRetVal.AppendFormat("DECLARE {0} AS DATETIME", item.ParameterName)
                                      sbRetVal.AppendLine()
                                      sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value)
                                      sbRetVal.AppendLine()
                      
                                  Case DbType.Guid
                                      sbRetVal.AppendFormat("DECLARE {0} AS UNIQUEIDENTIFIER", item.ParameterName)
                                      sbRetVal.AppendLine()
                                      sbRetVal.AppendFormat("SET {0} = '{1}'", item.ParameterName, item.Value)
                                      sbRetVal.AppendLine()
                      
                                  Case DbType.Int32
                                      sbRetVal.AppendFormat("DECLARE {0} AS int", item.ParameterName)
                                      sbRetVal.AppendLine()
                                      sbRetVal.AppendFormat("SET {0} = {1}", item.ParameterName, item.Value)
                                      sbRetVal.AppendLine()
                      
                                  Case Else
                                      Stop
                      
                              End Select
                          Next
                      
                          sbRetVal.AppendLine("")
                          sbRetVal.AppendLine(cmd.CommandText)
                      
                          Return sbRetVal.ToString()
                      End Function
                      

                      【讨论】:

                        【解决方案18】:

                        正如@pkExec 和@Alok 提到的,使用 Replace 在 100% 的情况下不起作用。 这是我在 DAL 中使用的解决方案,它使用 RegExp 仅“匹配整个单词”并正确格式化数据类型。因此生成的 SQL 可以直接在 MySQL Workbench(或 SQLSMS 等)中进行测试:)

                        (根据使用的 DBMS 替换 MySQLHelper.EscapeString() 函数。)

                        Dim query As String = cmd.CommandText
                        query = query.Replace("SET", "SET" & vbNewLine)
                        query = query.Replace("WHERE", vbNewLine & "WHERE")
                        query = query.Replace("GROUP BY", vbNewLine & "GROUP BY")
                        query = query.Replace("ORDER BY", vbNewLine & "ORDER BY")
                        query = query.Replace("INNER JOIN", vbNewLine & "INNER JOIN")
                        query = query.Replace("LEFT JOIN", vbNewLine & "LEFT JOIN")
                        query = query.Replace("RIGHT JOIN", vbNewLine & "RIGHT JOIN")
                        If query.Contains("UNION ALL") Then
                            query = query.Replace("UNION ALL", vbNewLine & "UNION ALL" & vbNewLine)
                        ElseIf query.Contains("UNION DISTINCT") Then
                            query = query.Replace("UNION DISTINCT", vbNewLine & "UNION DISTINCT" & vbNewLine)
                        Else
                            query = query.Replace("UNION", vbNewLine & "UNION" & vbNewLine)
                        End If
                        
                        For Each par In cmd.Parameters
                            If par.Value Is Nothing OrElse IsDBNull(par.Value) Then
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "NULL")
                            ElseIf TypeOf par.Value Is Date Then
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & Format(par.Value, "yyyy-MM-dd HH:mm:ss") & "'")
                            ElseIf TypeOf par.Value Is TimeSpan Then
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & par.Value.ToString & "'")
                            ElseIf TypeOf par.Value Is Double Or TypeOf par.Value Is Decimal Or TypeOf par.Value Is Single Then
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", Replace(par.Value.ToString, ",", "."))
                            ElseIf TypeOf par.Value Is Integer Or TypeOf par.Value Is UInteger Or TypeOf par.Value Is Long Or TypeOf par.Value Is ULong Then
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", par.Value.ToString)
                            Else
                                query = RegularExpressions.Regex.Replace(query, par.ParameterName & "\b", "'" & MySqlHelper.EscapeString(CStr(par.Value)) & "'")
                            End If
                        Next
                        

                        例子:

                        SELECT * FROM order WHERE order_status = @order_status AND order_date = @order_date
                        

                        将生成:

                        SELECT * FROM order WHERE order_status = 'C' AND order_date = '2015-01-01 00:00:00'
                        

                        【讨论】:

                          【解决方案19】:

                          sql 命令查询将使用 exec sp_executesql 执行,所以这里有另一种获取语句作为字符串的方法(SqlCommand 扩展方法):

                          public static string ToSqlStatement(this SqlCommand cmd)
                          {
                              return $@"EXECUTE sp_executesql N'{cmd.CommandText.Replace("'", "''")}'{cmd.Parameters.ToSqlParameters()}";
                          }
                          
                          private static string ToSqlParameters(this SqlParameterCollection col)
                          {
                              if (col.Count == 0)
                                  return string.Empty;
                              var parameters = new List<string>();
                              var parameterValues = new List<string>();
                              foreach (SqlParameter param in col)
                              {
                                  parameters.Add($"{param.ParameterName}{param.ToSqlParameterType()}");
                                  parameterValues.Add($"{param.ParameterName} = {param.ToSqlParameterValue()}");
                              }
                              return $",N\'{string.Join(",", parameters)}\',{string.Join(",", parameterValues)}";
                          }
                          
                          private static object ToSqlParameterType(this SqlParameter param)
                          {
                              var paramDbType = param.SqlDbType.ToString().ToLower();
                              if (param.Precision != 0 && param.Scale != 0)
                                  return $"{paramDbType}({param.Precision},{param.Scale})";
                              if (param.Precision != 0)
                                  return $"{paramDbType}({param.Precision})";
                              switch (param.SqlDbType)
                              {
                                  case SqlDbType.VarChar:
                                  case SqlDbType.NVarChar:
                                      string s = param.SqlValue?.ToString() ?? string.Empty;
                                      return paramDbType + (s.Length > 0 ? $"({s.Length})" : string.Empty);
                                  default:
                                      return paramDbType;
                              }
                          }
                          
                          private static string ToSqlParameterValue(this SqlParameter param)
                          {
                              switch (param.SqlDbType)
                              {
                                  case SqlDbType.Char:
                                  case SqlDbType.Date:
                                  case SqlDbType.DateTime:
                                  case SqlDbType.DateTime2:
                                  case SqlDbType.DateTimeOffset:
                                  case SqlDbType.NChar:
                                  case SqlDbType.NText:
                                  case SqlDbType.NVarChar:
                                  case SqlDbType.Text:
                                  case SqlDbType.Time:
                                  case SqlDbType.VarChar:
                                  case SqlDbType.Xml:
                                      return $"\'{param.SqlValue.ToString().Replace("'", "''")}\'";
                                  case SqlDbType.Bit:
                                      return param.SqlValue.ToBooleanOrDefault() ? "1" : "0";
                                  default:
                                      return param.SqlValue.ToString().Replace("'", "''");
                              }
                          }
                          
                          public static bool ToBooleanOrDefault(this object o, bool defaultValue = false)
                          {
                              if (o == null)
                                  return defaultValue;
                              string value = o.ToString().ToLower();
                              switch (value)
                              {
                                  case "yes":
                                  case "true":
                                  case "ok":
                                  case "y":
                                      return true;
                                  case "no":
                                  case "false":
                                  case "n":
                                      return false;
                                  default:
                                      bool b;
                                      if (bool.TryParse(o.ToString(), out b))
                                          return b;
                                      break;
                              }
                              return defaultValue;
                          }
                          

                          【讨论】:

                            【解决方案20】:

                            也需要涵盖非存储过程,因此我使用以下逻辑扩充了 CommandAsSql 库(请参阅上面@Flapper 答案下的 cmets):

                                private static void CommandAsSql_Text(this SqlCommand command, System.Text.StringBuilder sql)
                                {
                                    string query = command.CommandText;
                            
                                    foreach (SqlParameter p in command.Parameters)
                                        query = Regex.Replace(query, "\\B" + p.ParameterName + "\\b", p.ParameterValueForSQL()); //the first one is \B, the 2nd one is \b, since ParameterName starts with @ which is a non-word character in RegEx (see https://stackoverflow.com/a/2544661)
                            
                                    sql.AppendLine(query);
                                }
                            

                            拉取请求位于: https://github.com/jphellemons/CommandAsSql/pull/3/commits/527d696dc6055c5bcf858b9700b83dc863f04896

                            正则表达式的想法是基于上面@stambikk 和 EvZ 的 cmets 以及 https://stackoverflow.com/a/2544661/903783 的“更新:”部分,其中提到了“负面的后视断言”。在正则表达式的开头使用 \B 而不是 \b 进行单词边界检测是因为 p.parameterName 将始终以不是单词字符的“@”开头。

                            请注意,ParameterValueForSQL() 是在 CommandAsSql 库中定义的扩展方法,用于处理单引号字符串参数值等问题。

                            【讨论】:

                            • 顺便说一句,其他有希望的代码位于github.com/jeroenpot/SqlHelper/blob/master/Source/…(在此线程的答案中提到)。如果您发现其中一个或另一个不起作用,可能可以合并来自 SQLCommand 和 SqlGenerator 的代码
                            • ...意思是在最后一条评论中说CommandAsSQL库而不是SQLCommand
                            【解决方案21】:

                            如果你要转换命令文本:

                            Private Function ConvToNonParm(ByRef Cmd As SqlClient.SqlCommand) As String
                                For myCnt As Int16 = 1 To Cmd.Parameters.Count
                                    Dim myVal As String = Cmd.Parameters(myCnt - 1).Value
                                    Select Case Cmd.Parameters(myCnt - 1).SqlDbType
                                        Case SqlDbType.Char, SqlDbType.NChar, SqlDbType.VarChar, SqlDbType.NChar, SqlDbType.NVarChar 'and so on
                                            myVal = "'" & myVal & "'"
                                            'Case "others...."
                            
                                        Case Else
                                            'please assing
                                    End Select
                                    Cmd.CommandText = Replace(Cmd.CommandText, Cmd.Parameters(myCnt - 1).ToString, myVal)
                                Next
                                Cmd.Parameters.Clear()
                                Return Cmd.CommandText
                            End Function
                            

                            现在可以得到非参数commandtext如下:

                                myCmd.CommandText = "UPDATE someTable SET Value = @Value"
                                myCmd.CommandText &= " WHERE Id = @Id"
                                myCmd.Parameters.AddWithValue("@Id", 1234)
                                myCmd.Parameters.AddWithValue("@Value", "myValue")
                            
                                myCmd.CommandText = ConvToNonParm(myCmd)
                            

                            结果是“UPDATE someTable SET Value = 'myValue' WHERE Id = 1234”没有参数了

                            【讨论】:

                              【解决方案22】:

                              扩展 Kon's code 以帮助调试存储过程:

                                  private void ExtractSqlCommandForDebugging(SqlCommand cmd)
                                  {
                                      string sql = "exec " + cmd.CommandText;
                                      bool first = true;
                                      foreach (SqlParameter p in cmd.Parameters)
                                      {
                                          string value = ((p.Value == DBNull.Value) ? "null"
                                                          : (p.Value is string) ? "'" + p.Value + "'"
                                                          : p.Value.ToString());
                                          if (first)
                                          {
                                              sql += string.Format(" {0}={1}", p.ParameterName, value);
                                              first = false;
                                          }
                                          else
                                          {
                                              sql += string.Format("\n , {0}={1}", p.ParameterName, value);
                                          }
                                      }
                                      sql += "\nGO";
                                      Debug.WriteLine(sql);
                                  }
                              

                              在我的第一个测试用例中,它生成了:

                              exec dbo.MyStoredProcName @SnailMail=False
                               , @Email=True
                               , @AcceptSnailMail=False
                               , @AcceptEmail=False
                               , @DistanceMiles=-1
                               , @DistanceLocationList=''
                               , @ExcludeDissatisfied=True
                               , @ExcludeCodeRed=True
                               , @MinAge=null
                               , @MaxAge=18
                               , @GenderTypeID=-1
                               , @NewThisYear=-1
                               , @RegisteredThisYear=-1
                               , @FormersTermGroupList=''
                               , @RegistrationStartDate=null
                               , @RegistrationEndDate=null
                               , @DivisionList='25'
                               , @LocationList='29,30'
                               , @OneOnOneOPL=-1
                               , @JumpStart=-1
                               , @SmallGroup=-1
                               , @PurchasedEAP=-1
                               , @RedeemedEAP=-1
                               , @ReturnPlanYes=False
                               , @MinNetPromoter=-1
                               , @MinSurveyScore=-1
                               , @VIPExclusionTypes='-2'
                               , @FieldSelectionMask=65011584
                               , @DisplayType=0
                              GO
                              

                              您可能需要添加更多有条件的“..is...”类型分配,例如日期和时间。

                              【讨论】:

                                【解决方案23】:

                                一个班轮:

                                string.Join(",", from SqlParameter p in cmd.Parameters select p.ToString()) 
                                

                                【讨论】:

                                  【解决方案24】:

                                  从参数命令到非参数命令,你可以改变这个

                                  Using cmd As SqlCommand = Connection.CreateCommand
                                      cmd.CommandText = "UPDATE someTable SET Value = @Value"
                                      cmd.CommandText &= " WHERE Id = @Id"
                                      cmd.Parameters.AddWithValue("@Id", 1234)
                                      cmd.Parameters.AddWithValue("@Value", "myValue")
                                      cmd.ExecuteNonQuery
                                  End Using
                                  

                                  Private sub Update( byval myID as Int32, byval myVal as String)
                                      Using cmd As SqlCommand = Connection.CreateCommand
                                          cmd.CommandText = "UPDATE someTable SET Value = '" & myVaL & "'" & _
                                                            " WHERE Id = " & myID  
                                          cmd.ExecuteNonQuery
                                      End Using
                                  End sub
                                  

                                  【讨论】:

                                  • 这并没有回答将通用 SqlCommand 对象转换为字符串的问题,而是用硬编码的字符串替换它。它还在执行结果字符串,而不是返回它(例如用于日志记录) - 永远不能执行这样的字符串,并且应该被认为容易受到 SQL 注入的攻击。
                                  猜你喜欢
                                  • 2011-07-18
                                  • 2013-03-07
                                  • 2011-07-17
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2023-03-15
                                  • 2011-02-06
                                  • 2023-03-12
                                  • 2016-09-16
                                  相关资源
                                  最近更新 更多