C# 从 SqlCommand 对象中获取生成的 SQL 语句?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/265192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:29:07  来源:igfitidea点击:

Get the generated SQL statement from a SqlCommand object?

c#vb.netado.net

提问by dummy

I have the following code:

我有以下代码:

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

I wonder if there is any way to get the final SQL statment as a String, which should look like this:

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

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

If anyone wonders why I would do this:

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

  • for logging (failed) statements
  • for having the possibility to copy & paste it to the Enterprise Manager for testing purposes
  • 用于记录(失败)语句
  • 有可能将其复制并粘贴到企业管理器以进行测试

采纳答案by Flapper

Whilst not perfect, here's something I knocked up for TSQL - could be easily tweaked for other flavors... If nothing else it will give you a start point for your own improvements :)

虽然不完美,但这是我为 TSQL 提出的一些东西 - 可以很容易地针对其他风格进行调整......如果没有别的,它会给你一个你自己改进的起点:)

This does an OK job on data types and output parameters etc similar to using "execute stored procedure" in SSMS. We mostly used SPs so the "text" command doesn't account for parameters etc

这在数据类型和输出参数等方面做得很好,类似于在 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();
    }

this generates output along these lines...

这会沿着这些线生成输出......

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);

回答by Rockcoder

If you're using SQL Server, you could use SQL Server Profiler (if you have it) to view the command string that is actually executed. That would be useful for copy/paste testing purpuses but not for logging I'm afraid.

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

回答by Tomalak

You can't, because it does not generate any SQL.

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

The parameterized query (the one in CommandText) is sent to the SQL Server as the equivalent of a prepared statement. When you execute the command, the parameters and the query text are treated separately. At no point in time a complete SQL string is generated.

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

You can use SQL Profiler to take a look behind the scenes.

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

回答by Ed Guiness

Profiler is hands-down your best option.

Profiler 无疑是您的最佳选择。

You might need to copy a set of statements from profiler due to the prepare + execute steps involved.

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

回答by Kon

For logging purposes, I'm afraid there's no nicer way of doing this but to construct the string yourself:

出于记录目的,恐怕没有更好的方法可以做到这一点,只能自己构造字符串:

string query = cmd.CommandText;

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

回答by dummy

This solution works for me right now. Maybe it is usefull to someone. Please excuse all the redundancy.

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

    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

回答by Mitch

I needed a similar command to string transformer to allow for more verbose logging, so I wrote this one. It will produce the text needed to re-execute the command in a new session including output parameters and structured parameters. It is lightly tested, but caveat emptor.

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

Example:

例子:

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;

Will produce:

将产生:

-- 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

Implementation:

执行:

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(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("h2"));
                        }
                    }
                }
                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.VarChar:
            case SqlDbType.NVarChar:
            case SqlDbType.VarBinary:
                {
                    sbCommandText.Append(param.SqlDbType.ToString().ToUpper());
                    sbCommandText.Append("(MAX /* Specified as ");
                    sbCommandText.Append(param.Size);
                    sbCommandText.Append(" */)");
                }
                break;
            // fixed length
            case SqlDbType.Text:
            case SqlDbType.NText:
            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:
            case SqlDbType.Image:
                {
                    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;
        }
    }
}

回答by MPelletier

If it's only to check how a parameter is formatted in the result query, most DBMS's will allow querying literals from nothing. Thus:

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

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

That way you can see if quotes are doubled, etc.

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

回答by alan

I had the same exact question and after reading these responses mistakenly decided it wasn't possible to get the exact resulting query. I was wrong.

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

Solution:Open Activity Monitorin SQL Server Management Studio, narrow the processes section to the login username, database or application name that your application is using in the connection string. When the call is made to the db refresh Activity Monitor. When you see the process, right click on it and View Details.

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

Note, this may not be a viable option for a busy db. But you should be able to narrow the result considerably using these steps.

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

回答by nocarrier

This is what I use to output parameter lists for a stored procedure into the debug console:

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

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);

This will generate a console outputt simlar to this:

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

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

I place this code directly below any procedure I wish to debug and is similar to a sql profiler session but in C#.

我将此代码直接放在我希望调试的任何过程下方,类似于 sql 探查器会话,但在 C# 中。