8

从 .NET 环境中,我可以访问由SqlCommand对象生成的完整 SQL 字符串吗?

注意:完整的 SQL 字符串显示在 Intellisense 悬停中,在 VisualStudio 中,在调试模式下。

如果必须,我愿意使用反射技术。我敢肯定这里有人知道一种方法。


更新 1
我正在调用具有参数的存储过程,cmd.CommandType = CommandType.StoredProcedure并试图获取生成并运行的完整 SQL。我想知道如果cmd. Prepare()方法在这种情况下可能没有用,如果它可能将完整的字符串存储在状态字段或类似的东西中。


更新 2:

鉴于下面的答案(和引用)表明在准备或执行期间内部没有生成完整的 SQL 字符串,我使用 .NET Reflector 做了一些探索。甚至内部连接类似乎也传递对象而不是将它们归结为字符串,例如:

内部抽象 void AddPreparedCommand( SqlCommand cmd );
声明类型:System.Data.SqlClient.SqlInternalConnection
程序集:System.Data,版本=2.0.0.0


总的来说,感谢大家为证明可以做什么并展示实际发生的事情所付出的详细程度。非常感激。我喜欢详尽的解释;他们增加了保证并为答案提供了可信度。

4

5 回答 5

11

将所有参数名称替换为其值的简单循环将为您提供类似于最终结果的内容,但存在几个问题。

  1. 由于从未使用参数值实际重建 SQL,因此不需要考虑换行符和引号等内容
  2. 注释中的参数名称从未实际处理它们的值,而是保持原样

有了这些,并考虑到以相同字符开头的参数名称,例如@NAME@NAME_FULL,我们可以将所有参数名称替换为该参数位置的值:

string query = cmd.CommandText;
foreach (SqlParameter p in cmd.Parameters.OrderByDescending(p => p.ParameterName.Length))
{
    query = query.Replace(p.ParameterName, p.Value.ToString());
}

然而,这还有一个问题,如果一个参数是一个字符串,那么最初看起来像这样的 SQL:

SELECT * FROM yourtable WHERE table_code = @CODE

看起来像这样:

SELECT * FROM yourtable WHERE table_code = SOME CODE WITH SPACES

这显然是不合法的 SQL,因此我们还需要考虑一些参数类型:

DbType[] quotedParameterTypes = new DbType[] {
    DbType.AnsiString, DbType.Date,
    DbType.DateTime, DbType.Guid, DbType.String,
    DbType.AnsiStringFixedLength, DbType.StringFixedLength
};
string query = cmd.CommandText;

var arrParams = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(arrParams, 0);

foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
{
    string value = p.Value.ToString();
    if (quotedParameterTypes.Contains(p.DbType))
        value = "'" + value + "'";
    query = query.Replace(p.ParameterName, value);
}
于 2011-03-18T18:47:26.693 回答
6

这里有几个类似的问题。

这个问题提供了最有说服力的答案:How to get the generated SQL-Statment from a SqlCommand-Object?

答案是:

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

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

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

于 2011-03-18T19:22:55.300 回答
1

您的命令上的CommandText属性(或调用ToString())将为您提供所有 SQL,但有一个小例外。它肯定会给你在调试器中看到的任何东西。请注意,这不会为您提供参数,但会为您提供实际命令。

唯一需要注意的是,当CommandType是时Text,ADO.NET 框架将经常(事实上,几乎总是)使用sp_executesql来执行命令,而不是直接针对连接执行命令。从这个意义上说,不可能获得执行的确切SQL。

于 2011-03-18T18:49:59.933 回答
0

我还没有尝试过,但如果你愿意使用 SMO,你也许可以使用 Capture Mode:

http://msdn.microsoft.com/en-us/library/ms162182(v=sql.120).aspx

于 2014-10-28T19:46:32.587 回答
0

我喜欢耶稣拉莫斯的回答,但我需要对输出参数的支持。(我还使用了字符串生成器来生成内容。)

为输出参数声明参数

 foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
    {
        // todo : I only needed a couple of types supported, you could add addition types
        string dbtype = string.Empty;
        switch (p.DbType)
        {
            case DbType.Guid:
                dbtype = "uniqueidentifier";
                break;
            case DbType.Int16:
            case DbType.Int64:
            case DbType.Int32:
                dbtype = "int";
                break;
            case DbType.String:
                dbtype = "varchar(max)";
                break;
        }

        query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
    }

构建主参数区

foreach (SqlParameter p in arrParams)
    {
        bool isLast = p == last;
        string value = p.Value.ToString();
        if (quotedParameterTypes.Contains(p.DbType))
            value = "'" + value + "'";
        if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
        {
            query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
        }
        else
        {
            query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
        }
    }

列出输出参数结果

foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
    {
        query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
    }

完整代码:

        public static string GetProcedureDebugInformation(SqlCommand cmd, [System.Runtime.CompilerServices.CallerMemberName] string caller = null, [System.Runtime.CompilerServices.CallerFilePath] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumber] int? lineNumber = null)
    {
        // Collection of parameters that should use quotes
        DbType[] quotedParameterTypes = new DbType[] {
            DbType.AnsiString, DbType.Date,
            DbType.DateTime, DbType.Guid, DbType.String,
            DbType.AnsiStringFixedLength, DbType.StringFixedLength
        };

        // String builder to contain generated string
        StringBuilder query = new StringBuilder();

        // Build some debugging information using free compiler information
        query.Append(filePath != null ? filePath : ""
        + (lineNumber.HasValue ? lineNumber.Value.ToString() : "")
        + (lineNumber.HasValue || !string.IsNullOrWhiteSpace(filePath) ? "\n\n" : ""));
        query.Append("\n\n");

        var arrParams = new SqlParameter[cmd.Parameters.Count];
        cmd.Parameters.CopyTo(arrParams, 0);

        // Declare Parameter for output parameters
        foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
        {
            // todo : I only needed a couple of types supported, you could add addition types
            string dbtype = string.Empty;
            switch (p.DbType)
            {
                case DbType.Guid:
                    dbtype = "uniqueidentifier";
                    break;
                case DbType.Int16:
                case DbType.Int64:
                case DbType.Int32:
                    dbtype = "int";
                    break;
                case DbType.String:
                    dbtype = "varchar(max)";
                    break;
            }

            query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
        }

        // Set Exec Text
        query.Append(string.Format("\n exec {0}\n", cmd.CommandText));
        var last = arrParams.LastOrDefault();

        //Build Main Parameter Area
        foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
        {
            bool isLast = p == last;
            string value = p.Value.ToString();
            if (quotedParameterTypes.Contains(p.DbType))
                value = "'" + value + "'";
            if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
            {
                query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
            }
            else
            {
                query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
            }
        }

        // List Output Parameter results
        foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
        {
            query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
        }

        return query.ToString();
    }
于 2018-03-04T22:33:18.727 回答