Lấy câu lệnh SQL được tạo từ một đối tượng SqlCommand?


186

Tôi có đoạn mã sau:

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

Tôi tự hỏi liệu có cách nào để có được thống kê SQL cuối cùng dưới dạng Chuỗi hay không, nó sẽ trông như thế này:

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

Nếu có ai thắc mắc tại sao tôi lại làm điều này:

  • để ghi nhật ký (không thành công)
  • để có khả năng sao chép và dán nó vào Trình quản lý doanh nghiệp cho mục đích thử nghiệm

1
Tại sao bạn đánh dấu stackoverflow.com/a/265261/206730 nếu không phân biệt giữa các kiểu dữ liệu khác nhau, Sql Injection, tên tham số tương tự (vấn đề thay thế) ...?
Kiquenet

@Kiquenet Tôi có thể đã tuyên thệ, rằng tôi đã thử điều đó nhưng nó đã không cho phép tôi. Bây giờ nó hoạt động. Cảm ơn vì điều này.
nộm

Nếu bạn muốn tạo chính xác SQL sẽ được chạy, hãy xem TdsParser.TdsExecuteRPC ( github.com/Microsoft/referencesource/blob/master/System.Data/ tựa ) và một chút sợ hãi.
Rory

Câu trả lời:


110

Mặc dù không hoàn hảo, nhưng đây là thứ tôi đánh bại cho TSQL - có thể dễ dàng điều chỉnh cho các hương vị khác ... Nếu không có gì khác, nó sẽ cho bạn một điểm khởi đầu cho những cải tiến của riêng bạn :)

Điều này thực hiện công việc OK đối với các kiểu dữ liệu và tham số đầu ra, vv tương tự như sử dụng "thực thi thủ tục được lưu trữ" trong SSMS. Chúng tôi chủ yếu sử dụng SP nên lệnh "văn bản" không tính đến các tham số, v.v.

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

điều này tạo ra đầu ra dọc theo các dòng này ...

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

7
Công việc tốt đẹp thực sự đang cố gắng giải quyết vấn đề ở đây, bỏ phiếu cho nỗ lực một mình.
Adam Tolley

3
Phương thức "ToBooleanOrDefault (false)" của bạn là gì?
Benoittr

6
@Benoittr, bạn có thể thấy một triển khai ToBooleanOrDefaulttại đây: Câu hỏi # 3244850
Alexandre Marcondes

@flapper những gì của một trường blob hoặc mảng byte
Smith

1
Thực hiện một số điều chỉnh nhỏ và thêm các tham số giá trị bảng. Tất cả đều có trên GitHub và gói Nuget .Net Standard 2.0 github.com/jphellemons/CommandAsSql Cảm ơn bạn Flapper! Tôi có thể thêm bạn làm cộng tác viên không?
JP Hellemons

128

Đối với mục đích ghi nhật ký, tôi e rằng không có cách nào tốt hơn để làm điều này ngoài việc tự xây dựng chuỗi:

string query = cmd.CommandText;

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

Nếu tôi làm điều đó, tôi sẽ phải phân biệt giữa các kiểu dữ liệu khác nhau. Sau đó, tôi có thể bỏ qua tất cả các truy vấn tham số hóa và thực hiện điều đó.
nộm

2
hình nộm: không hẳn nếu bạn thực hiện một tuyên bố đã chuẩn bị, bạn có nguy cơ bị tấn công tiêm sql. +1 cho câu trả lời.
Sunny Milenov

11
Có một gotcha ở đây. Nếu tôi có "Tham số" và "differParam" làm tham số, thì nó sẽ hiển thị sự khác biệt vô dụng khi nó thay thế nó thành "ValueParam". giả sử Param = Giá trị.
Alok

5
Câu hỏi không liên quan đến các kỹ thuật mã hóa phòng thủ, do đó kiểm tra tham chiếu null không phải là một phần của câu trả lời. Thực tế là nó nên được thực hiện là ngụ ý, do đó tôi không xem đây là một nhận xét mang tính xây dựng.
Kon

2
một cách tiếp cận tốt hơn một chút để loại bỏ vấn đề với các tên param tương tự được chỉ ra bởi @Alok có thể là sử dụng để sử dụng query = Regex.Replace(query, @"\b" + p.ParameterName + @"\b", p.Value.ToString());để thay thế các param trong chuỗi. Điều này sẽ thay thế 'toàn bộ từ'. Nó có thể không phải là một giải pháp phổ quát mặc dù \ b đánh dấu vị trí giữa một ký tự từ và ký tự không phải từ, vì vậy trong trường hợp tên tham số của bạn bắt đầu bằng @, bạn nên sử dụng p.ParameterName + @"\b"để thay thế param trong chuỗi truy vấn.
stambikk

47

Bạn không thể, bởi vì nó không tạo ra bất kỳ SQL nào.

Truy vấn được tham số hóa (truy vấn CommandText) được gửi đến Máy chủ SQL tương đương với câu lệnh được chuẩn bị. Khi bạn thực hiện lệnh, các tham số và văn bản truy vấn được xử lý riêng. Tại một thời điểm, một chuỗi SQL hoàn chỉnh được tạo ra.

Bạn có thể sử dụng SQL Profiler để xem hậu trường.


6
SQL được tạo - tìm trong Profiler - đó là văn bản tôi muốn có cho mục đích ghi nhật ký
kpkpkp

ngoài SQL Profiler (đang bị phản đối cho SQL Server mới hơn nếu tôi hiểu đúng một số nhận xét về MS) cũng có thể sử dụng Activity Monitor theo câu trả lời khác tại đây
George Birbilis

27

Tôi cần một lệnh tương tự với biến áp chuỗi để cho phép ghi nhật ký dài hơn, vì vậy tôi đã viết lệnh này. Nó sẽ tạo ra văn bản cần thiết để thực hiện lại lệnh trong một phiên mới bao gồm các tham số đầu ra và các tham số có cấu trúc. Nó được thử nghiệm nhẹ, nhưng emptor caveat.

Thí dụ:

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;

Sẽ sản xuất:

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

Thực hiện:

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

Cảm ơn vì điều này, nó khá toàn diện! :-)
Alastair Maw

Chính xác những gì tôi đang tìm kiếm, Cảm ơn.
Xilmiki

Tôi đã sử dụng điều này như một điểm khởi đầu cho một phiên bản của nó đã sử dụng sp_executesql để xử lý các tham số trong một câu lệnh thay vì khai báo các biến riêng biệt. Mã này thực sự đã chăm sóc tất cả các công việc tẻ nhạt và tôi chỉ phải sắp xếp lại các mảnh. Cảm ơn nhiều!
pettys

1
Điều này có yêu cầu tiền tố "N" cho chuỗi ký tự SQL không? Nếu không, bạn có thể nhận được nhiều "?". Thầm lặng. Xấu. (Ít nhất là với SQL Server 2005 - chưa được kiểm tra với các phiên bản ít cổ hơn.)
Paul Groke

@PaulGroke, bắt tốt. Tôi đã cập nhật để bao gồm Ntiền tố.
Mitch

6

Tôi cũng gặp vấn đề này khi một số truy vấn hoặc sp được tham số hóa sẽ cung cấp cho tôi một SqlException (chủ yếu là dữ liệu chuỗi hoặc nhị phân sẽ bị cắt ngắn) và các câu lệnh khó gỡ lỗi (Theo như tôi biết hiện tại không có hỗ trợ trình biên dịch sql cho SQL Azure)

Tôi thấy rất nhiều mã mô phỏng trong các phản ứng ở đây. Cuối cùng tôi đã đưa giải pháp của mình vào một dự án Thư viện Sql để sử dụng trong tương lai.

Trình tạo có sẵn tại đây: https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.L Library / SQLGenerator.cs

Nó hỗ trợ cả CommandType.Text và CommandType.StoredProcedure

Và nếu bạn cài đặt gói nuget, bạn có thể tạo nó với câu lệnh này:

SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters);

Không quá tệ, ít nhất nó liệt kê các giá trị cho từng thông số, nhưng vẫn không thực sự điền vào các giá trị. Ít nhất tôi có thể sử dụng notepad để tự làm điều đó, cảm ơn!
Harvey Lin

5

Nếu bạn đang sử dụng SQL Server, bạn có thể sử dụng SQL Server Profiler (nếu bạn có) để xem chuỗi lệnh thực sự được thực thi. Điều đó sẽ hữu ích cho việc sao chép / dán thử nghiệm purpuses nhưng không phải để đăng nhập tôi sợ.


3

Trả lời muộn, tôi biết nhưng tôi cũng muốn điều này để tôi có thể đăng nhập SQL. Sau đây là ngắn và đáp ứng nhu cầu của tôi.

Sau đây tạo ra SQL bạn có thể sao chép / dán trong SSMS (nó thay thế các tham số bằng các giá trị đúng cách). Bạn có thể thêm nhiều loại nhưng điều này đáp ứng tất cả những gì tôi sử dụng trong trường hợp này.

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

Bây giờ tôi có thể đăng nhập SQL ngay trước khi tôi thực thi nó:

LogSQL(queryCmd)
queryCmd.ExecuteNonQuery()

2

Profiler là lựa chọn tốt nhất của bạn.

Bạn có thể cần phải sao chép một tập hợp các câu lệnh từ hồ sơ do các bước chuẩn bị + thực hiện liên quan.


2

Tôi đã có cùng một câu hỏi chính xác và sau khi đọc những câu trả lời này đã quyết định nhầm, không thể có được truy vấn kết quả chính xác. Tôi đã sai.

Giải pháp: Mở Activity Monitortrong SQL Server Management Studio, thu hẹp phần quy trình để tên người dùng đăng nhập, cơ sở dữ liệu hoặc tên ứng dụng mà ứng dụng của bạn đang sử dụng trong chuỗi kết nối. Khi cuộc gọi được thực hiện để làm mới db Activity Monitor. Khi bạn thấy quá trình, nhấp chuột phải vào nó và View Details.

Lưu ý, đây có thể không phải là một lựa chọn khả thi cho một db bận rộn. Nhưng bạn sẽ có thể thu hẹp kết quả đáng kể bằng cách sử dụng các bước này.


2

Đã sử dụng một phần mã của Flapper cho giải pháp của tôi, trả về toàn bộ chuỗi SQL bao gồm các giá trị tham số để chạy trong 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;
    }

'Giải pháp' của bạn không hoạt động. Bạn đã thay thế \ r và \ n bằng "" khi bạn nên sử dụng "". Hơn nữa, nó không hoạt động nếu bạn có nhiều hơn 9 tham số kể từ khi thay thế '@ p1' thay thế cả '@ p1' và '@ p10' bằng tất cả các loại kết quả điên rồ. Sao chép danh sách tham số và đảo ngược nó là một sửa chữa nhanh chóng cho những gì tôi đang làm.
BH

Ngoài ra, mã của bạn sẽ không hoạt động cho lệnh cập nhật vì thay thế 'là null'.
BH

thực sự mã của Flapper không xử lý DBNull, có một vấn đề ở đây đối với thư viện CommandAsQuery dựa trên nó: github.com/jphellemons/CommandAsSql/issues/1
George Birbilis

2

Giải pháp của tôi:

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

2

Tôi đã viết phương pháp này cho tôi. Tôi sử dụng một phần mã của Bruno Ratnieks . Có lẽ nó hữu ích cho ai đó.

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

1

Nếu chỉ để kiểm tra cách một tham số được định dạng trong truy vấn kết quả, hầu hết các DBMS sẽ cho phép truy vấn bằng chữ từ không có gì. Như vậy:

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

Bằng cách đó bạn có thể thấy nếu trích dẫn được nhân đôi, v.v.


1

Đây là những gì tôi sử dụng để xuất danh sách tham số cho một thủ tục được lưu trữ vào bảng điều khiển gỡ lỗi:

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

Điều này sẽ tạo ra một giao diện điều khiển xuất ra simlar này:

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

Tôi đặt mã này trực tiếp bên dưới bất kỳ thủ tục nào tôi muốn gỡ lỗi và tương tự như phiên trình lược tả sql nhưng trong C #.


1

Phiên bản sửa đổi của câu trả lời của Kon vì nó chỉ hoạt động một phần với các tham số được đặt tên tương tự. Mặt trái của việc sử dụng chức năng Thay thế chuỗi. Ngoài ra, tôi cho anh ta tín dụng đầy đủ về giải pháp.

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

0

Giải pháp này hiệu quả với tôi ngay bây giờ. Có lẽ nó hữu ích với ai đó. Xin thứ lỗi tất cả sự dư thừa.

    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

0

Như @pkExec và @Alok đã đề cập, sử dụng Thay thế không hoạt động trong 100% trường hợp. Đây là giải pháp tôi đã sử dụng trong DAL của chúng tôi, chỉ sử dụng RegExp để "khớp toàn bộ từ" và định dạng chính xác các kiểu dữ liệu. Do đó, SQL được tạo có thể được kiểm tra trực tiếp trong MySQL Workbench (hoặc SQLSMS, v.v ...) :)

(Thay thế hàm MySQLHelper.EscapeString () theo DBMS được sử dụng.)

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

Thí dụ:

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

Sẽ được tạo:

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

0

các truy vấn lệnh sql sẽ được thực thi với exec sp_executesql, vì vậy đây là một cách khác để lấy câu lệnh dưới dạng chuỗi (phương thức mở rộng 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;
}

0

cũng cần thiết để bao gồm các thủ tục không được lưu trữ vì vậy tôi đã tăng cường thư viện CommandAsSql (xem các bình luận dưới câu trả lời của @ Flapper ở trên) với logic này:

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

yêu cầu kéo là tại: https://github.com/jphellemons/CommandAsSql/pull/3/commits/527d696dc6055c5bcf858b9700b83dc863f04896

ý tưởng Regex được dựa trên ý kiến ​​của @ stambikk và EvZ ở trên và phần "Cập nhật:" của https://stackoverflow.com/a/2544661/903783 đề cập đến "khẳng định nhìn phía sau tiêu cực". Việc sử dụng \ B thay vì \ b để phát hiện ranh giới từ khi bắt đầu biểu thức chính là vì p.parameterName sẽ luôn bắt đầu bằng "@" không phải là ký tự từ.

lưu ý rằng ParameterValueForQuery () là một phương thức mở rộng được xác định tại thư viện CommandAsSql để xử lý các vấn đề như các giá trị tham số chuỗi trích dẫn đơn, v.v.


btw, đoạn mã đầy hứa hẹn khác có tại github.com/jeroenpot/SqlHelper/blob/master/Source/, (được đề cập tại một câu trả lời trong chủ đề này). Có lẽ có thể hợp nhất mã từ SQLCommand và SqlGenerator nếu bạn tìm thấy thứ gì đó không hoạt động ở cái này hay cái khác
George Birbilis

... có nghĩa là nói thư viện CommandAsQuery thay vì SQLCommand trong bình luận cuối cùng
George Birbilis

0

Nếu bạn sẽ chuyển đổi lệnhtext:

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

Bây giờ bạn có thể nhận được lệnh không tham số như sau:

    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)

và Kết quả là "CẬP NHẬT giá trị SETTable = 'myValue' WHERE Id = 1234" không có tham số nữa


0

Mã mở rộng của Kon để giúp gỡ lỗi một thủ tục được lưu trữ:

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

Trong trường hợp thử nghiệm đầu tiên của tôi, nó đã tạo ra:

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

Bạn có thể sẽ cần thêm một số bài tập loại "..is ..." có điều kiện hơn, ví dụ như ngày và giờ.


-1

Lót:

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

-1

Từ lệnh tham số đến lệnh không tham số, Bạn có thể thay đổi lệnh này

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

Đến

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.