Dapatkan pernyataan SQL yang dihasilkan dari objek SqlCommand?

186

Saya memiliki kode berikut:

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

Saya ingin tahu apakah ada cara untuk mendapatkan statemen SQL final sebagai sebuah String, yang akan terlihat seperti ini:

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

Jika ada yang bertanya-tanya mengapa saya melakukan ini:

  • untuk mencatat (gagal) pernyataan
  • karena memiliki kemungkinan untuk menyalin & menempelkannya ke Enterprise Manager untuk tujuan pengujian
boneka
sumber
1
Mengapa Anda menandai jawaban stackoverflow.com/a/265261/206730 jika tidak membedakan antara tipe data yang berbeda, Sql Injection, nama parameter yang serupa (ganti masalah) ...?
Kiquenet
@ Kiquenet saya bisa bersumpah, bahwa saya mencobanya tetapi tidak membiarkan saya. Sekarang berhasil. Terima kasih untuk ini.
dummy
Jika Anda ingin menghasilkan SQL secara akurat yang akan dijalankan, lihat TdsParser.TdsExecuteRPC ( github.com/Microsoft/referencesource/blob/master/System.Data/… ) dan menjadi sedikit takut.
Rory

Jawaban:

110

Meskipun tidak sempurna, ini adalah sesuatu yang saya dapatkan untuk TSQL - bisa dengan mudah diubah untuk rasa lain ... Jika tidak ada yang lain, itu akan memberi Anda titik awal untuk perbaikan Anda sendiri :)

Ini melakukan pekerjaan OK pada tipe data dan parameter output dll mirip dengan menggunakan "jalankan prosedur tersimpan" di SSMS. Kami kebanyakan menggunakan SPs sehingga perintah "teks" tidak memperhitungkan parameter dll

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

ini menghasilkan output di sepanjang garis ini ...

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);
Flapper
sumber
7
Pekerjaan yang bagus sebenarnya mencoba untuk mengatasi masalah di sini, dipilih untuk usaha sendiri.
Adam Tolley
3
Apa yang akan menjadi metode "ToBooleanOrDefault (false)" Anda?
Benoittr
6
@Benoittr, Anda dapat melihat implementasi di ToBooleanOrDefaultsini: Pertanyaan # 3244850
Alexandre Marcondes
@flapper apa dari bidang gumpalan atau byte array
Smith
1
Melakukan beberapa penyesuaian kecil dan menambahkan parameter nilai tabel. Semuanya ada di GitHub dan paket .Net Standard 2.0 Nugget github.com/jphellemons/CommandAsSql Terima kasih Flapper! Bisakah saya menambahkan Anda sebagai kolaborator?
JP Hellemons
128

Untuk tujuan logging, saya khawatir tidak ada cara yang lebih baik untuk melakukan ini selain membuat sendiri string:

string query = cmd.CommandText;

foreach (SqlParameter p in cmd.Parameters)
{
    query = query.Replace(p.ParameterName, p.Value.ToString());
}
Kon
sumber
Jika saya melakukan itu, saya harus membedakan antara tipe data yang berbeda. Lalu saya bisa melewatkan query parameterized bersama-sama dan menjalankannya.
dummy
2
dummy: tidak juga. jika Anda menjalankan pernyataan yang sudah disiapkan, Anda berisiko terhadap serangan injeksi sql. +1 untuk jawabannya.
Sunny Milenov
11
Ada gotcha di sini. Jika saya memiliki "Param", dan "differentParam" sebagai parameter, itu membuat differentParam tidak berguna karena menggantikannya ke "ValueParam". dengan asumsi Param = Nilai.
Alok
5
Pertanyaannya tidak berhubungan dengan teknik pengkodean defensif, oleh karena itu pemeriksaan referensi nol bukan bagian dari jawabannya. Fakta bahwa itu harus diimplementasikan tersirat, oleh karena itu saya tidak melihat ini sebagai komentar yang konstruktif.
Kon
2
pendekatan yang sedikit lebih baik untuk menghilangkan masalah dengan nama-nama param serupa yang ditunjukkan oleh @Alok mungkin digunakan query = Regex.Replace(query, @"\b" + p.ParameterName + @"\b", p.Value.ToString());untuk mengganti params dalam string. Ini akan menggantikan 'seluruh kata'. Ini mungkin bukan solusi universal meskipun sebagai \ b menandai posisi antara karakter kata dan karakter non-kata jadi jika nama parameter Anda mulai dengan @, Anda harus menggunakan p.ParameterName + @"\b"untuk mengganti param dalam string kueri.
stambikk
47

Anda tidak bisa, karena tidak menghasilkan SQL apa pun.

Permintaan parameterisasi (yang masuk CommandText) dikirim ke SQL Server sebagai setara dengan pernyataan yang disiapkan. Saat Anda menjalankan perintah, parameter dan teks kueri diperlakukan secara terpisah. Pada titik waktu string SQL lengkap dihasilkan.

Anda dapat menggunakan SQL Profiler untuk melihat di belakang layar.

Tomalak
sumber
6
SQL dihasilkan - lihat di Profiler - itulah teks yang ingin saya miliki untuk tujuan logging
kpkpkp
selain dari SQL Profiler (yang tidak digunakan lagi untuk SQL Server yang lebih baru jika saya mengerti beberapa komentar MS dengan benar) juga dapat menggunakan Activity Monitor sesuai dengan jawaban lain di sini
George Birbilis
27

Saya membutuhkan perintah serupa dengan transformator string untuk memungkinkan lebih banyak logging verbose, jadi saya menulis ini. Ini akan menghasilkan teks yang diperlukan untuk menjalankan kembali perintah dalam sesi baru termasuk parameter output dan parameter terstruktur. Ini diuji ringan, tetapi peringatan emptor.

Contoh:

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;

Akan menghasilkan:

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

Penerapan:

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;
        }
    }
}
Mitch
sumber
Terima kasih untuk ini, ini cukup komprehensif! :-)
Alastair Maw
Persis apa yang saya cari, Terima kasih.
Xilmiki
Saya menggunakan ini sebagai titik awal untuk versi yang menggunakan sp_executesql untuk menangani parameter dalam satu pernyataan daripada mendeklarasikan variabel secara terpisah. Kode ini benar-benar menangani semua pekerjaan yang membosankan dan saya hanya perlu mengatur ulang bagian-bagiannya. Terima kasih banyak!
Pettys
1
Bukankah ini memerlukan awalan "N" untuk literal string SQL? Kalau tidak, Anda mungkin mendapatkan banyak "?" Diam-diam. Buruk. (Setidaknya dengan SQL Server 2005 - belum diperiksa dengan versi yang kurang kuno.)
Paul Groke
@ PaulGroke, tangkapan yang bagus. Saya telah memperbarui untuk menyertakan Nawalan.
Mitch
6

Saya juga memiliki masalah ini di mana beberapa pertanyaan parameter atau sp akan memberi saya SqlException (sebagian besar string atau data biner akan terpotong), dan pernyataan di mana sulit untuk debug (Sejauh yang saya tahu saat ini tidak ada dukungan sql-profiler untuk SQL Azure)

Saya melihat banyak kode simular dalam reaksi di sini. Saya akhirnya menempatkan solusi saya dalam proyek Sql-Library untuk digunakan di masa depan.

Generator tersedia di sini: https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.Library/SqlGenerator.cs

Ini mendukung CommandType.Text dan CommandType.StoredProcedure

Dan jika Anda menginstal paket nuget, Anda dapat membuatnya dengan pernyataan ini:

SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters);
Jeroen Pot
sumber
Tidak terlalu buruk, setidaknya daftar nilai untuk setiap param, tetapi masih belum benar-benar mengisi nilai-nilai. Setidaknya saya bisa menggunakan notepad untuk melakukannya sendiri, terima kasih!
Harvey Lin
5

Jika Anda menggunakan SQL Server, Anda bisa menggunakan SQL Server Profiler (jika Anda memilikinya) untuk melihat string perintah yang sebenarnya dieksekusi. Itu akan berguna untuk tujuan pengujian salin / rekat tetapi tidak untuk pencatatan, saya khawatir.

Rockcoder
sumber
3

Jawaban terlambat, saya tahu tetapi saya juga menginginkan ini sehingga saya bisa mencatat SQL. Berikut ini singkat dan memenuhi kebutuhan saya.

Berikut ini menghasilkan SQL Anda dapat menyalin / menempel di SSMS (ini mengganti parameter dengan nilai-nilai dengan benar). Anda dapat menambahkan lebih banyak jenis tetapi ini memenuhi semua yang saya gunakan dalam kasus ini.

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

Sekarang saya dapat mencatat SQL sebelum saya menjalankannya:

LogSQL(queryCmd)
queryCmd.ExecuteNonQuery()
Paul Sturm
sumber
2

Profiler adalah pilihan terbaik Anda.

Anda mungkin perlu menyalin seperangkat pernyataan dari profiler karena langkah persiapan + eksekusi yang terlibat.

Ed Guinness
sumber
2

Saya memiliki pertanyaan yang persis sama dan setelah membaca tanggapan ini secara keliru memutuskan bahwa tidak mungkin untuk mendapatkan permintaan yang tepat. Saya salah.

Solusi: Buka Activity Monitordi SQL Server Management Studio, mempersempit bagian proses untuk username login, database atau nama aplikasi yang aplikasi Anda menggunakan di connection string. Ketika panggilan dibuat untuk menyegarkan db Activity Monitor. Ketika Anda melihat prosesnya, klik kanan padanya dan View Details.

Catatan, ini mungkin bukan opsi yang layak untuk db yang sibuk. Tetapi Anda harus dapat mempersempit hasilnya dengan menggunakan langkah-langkah ini.

alan
sumber
2

Digunakan bagian dari kode Flapper untuk solusi saya, yang mengembalikan seluruh string SQL termasuk nilai parameter untuk dijalankan di 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;
    }
Barry-Dean
sumber
'Solusi' Anda tidak berhasil. Anda mengganti \ r dan \ n dengan "" ketika Anda seharusnya menggunakan "". Lebih lanjut, itu tidak berfungsi jika Anda memiliki lebih dari 9 parameter karena mengganti '@ p1' menggantikan '@ p1' dan '@ p10' dengan semua jenis hasil yang gila. Menyalin daftar parameter dan membalikkannya adalah perbaikan cepat untuk apa yang saya lakukan.
BH
Selain itu, kode Anda tidak akan berfungsi untuk perintah pembaruan karena penggantian 'nol'.
BH
memang kode Flapper tidak menangani DBNull, ada masalah di sini untuk pustaka CommandAsSQL yang didasarkan pada itu: github.com/jphellemons/CommandAsSql/issues/1
George Birbilis
2

Solusi Saya:

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()));
    }
}
Martin.Martinsson
sumber
2

Saya menulis metode ini untuk saya. Saya menggunakan beberapa bagian dari kode Bruno Ratnieks . Mungkin bermanfaat bagi seseorang.

 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();
    }
Daghan Karakasoglu
sumber
1

Jika hanya untuk memeriksa bagaimana parameter diformat dalam kueri hasil, sebagian besar DBMS akan mengizinkan kueri literal dari nol. Jadi:

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

Dengan begitu Anda dapat melihat apakah penawaran dilipatgandakan, dll.

MPelletier
sumber
1

Ini yang saya gunakan untuk menampilkan daftar parameter untuk prosedur tersimpan ke konsol debug:

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

Ini akan menghasilkan keluaran konsol yang mirip dengan ini:

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

Saya menempatkan kode ini langsung di bawah prosedur yang ingin saya debug dan mirip dengan sesi profiler sql tetapi dalam C #.

tanpa bawaan
sumber
1

Versi modifikasi dari jawaban Kon karena hanya berfungsi sebagian dengan parameter bernama serupa. Sisi bawah menggunakan fungsi String Replace. Selain itu, saya memberinya kredit penuh untuk solusinya.

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;
}
Rumah kucing
sumber
0

Solusi ini bekerja untuk saya saat ini. Mungkin bermanfaat bagi seseorang. Maafkan semua redundansi.

    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
boneka
sumber
0

Seperti @pkExec dan @Alok sebutkan, gunakan Ganti tidak berfungsi dalam 100% kasus. Ini adalah solusi yang saya gunakan di DAL kami yang menggunakan RegExp untuk "mencocokkan seluruh kata" saja dan memformat tipe data dengan benar. Dengan demikian SQL yang dihasilkan dapat diuji secara langsung di MySQL Workbench (atau SQLSMS, dll ...) :)

(Ganti fungsi MySQLHelper.EscapeString () sesuai dengan DBMS yang digunakan.)

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

Contoh:

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

Akan dihasilkan:

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

kueri perintah sql akan dieksekusi dengan exec sp_executesql, jadi inilah cara lain untuk mendapatkan pernyataan sebagai string (metode ekstensi 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;
}
o_link
sumber
0

diperlukan untuk mencakup prosedur yang tidak tersimpan juga jadi saya menambah pustaka CommandAsSql (lihat komentar di bawah jawaban @ Flapper di atas) dengan logika ini:

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

permintaan tarik ada di: https://github.com/jphellemons/CommandAsSql/pull/3/commits/527d696dc6055c5bcf858b9700b83dc863f04896

ide Regex didasarkan pada komentar @ stambikk dan EvZ di atas dan bagian "Pembaruan:" dari https://stackoverflow.com/a/2544661/903783 yang menyebutkan "pernyataan negatif melihat ke belakang". Penggunaan \ B sebagai ganti \ b untuk deteksi batas kata di awal ekspresi reguler adalah karena p.parameterName akan selalu dimulai dengan "@" yang bukan karakter kata.

perhatikan bahwa ParameterValueForSQL () adalah metode ekstensi yang didefinisikan di perpustakaan CommandAsSql untuk menangani masalah seperti nilai parameter string kutip tunggal dll.

George Birbilis
sumber
btw, potongan kode lain yang menjanjikan adalah di github.com/jeroenpot/SqlHelper/blob/master/Source/… (disebutkan pada jawaban di utas ini). Mungkin dapat menggabungkan kode dari SQLCommand dan SqlGenerator jika Anda menemukan sesuatu yang tidak berfungsi pada satu atau yang lain
George Birbilis
... dimaksudkan untuk mengatakan perpustakaan CommandAsSQL bukannya SQLCommand dalam komentar terakhir
George Birbilis
0

Jika Anda akan mengkonversi commandtext:

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

Sekarang Anda bisa mendapatkan commandtext non parameter sebagai berikut:

    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)

dan hasilnya adalah "UPDATE someTable SET Value = 'myValue' WHERE Id = 1234" tanpa parameter lagi

pengguna11982798
sumber
0

Kode Extended Kon untuk membantu men-debug prosedur yang tersimpan:

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

Dalam uji kasus pertama saya, ini menghasilkan:

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

Anda mungkin perlu menambahkan beberapa tugas tipe "..is ..." yang lebih bersyarat, misalnya untuk tanggal dan waktu.

CAK2
sumber
-1

Satu liner:

string.Join(",", from SqlParameter p in cmd.Parameters select p.ToString()) 
CheesusCrust
sumber
-1

Dari perintah parameter ke perintah non-parameter, Anda dapat mengubah yang ini

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

Untuk

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