SqlCommand nesnesinden oluşturulan SQL deyimi al?


186

Takip koduna sahibim:

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

Bunun gibi görünmelidir bir String olarak son SQL deyimi almak için herhangi bir yolu olup olmadığını merak ediyorum:

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

Birisi neden bunu yapacağımı merak ederse:

  • günlüğe kaydetme (başarısız) ifadeleri için
  • test amacıyla kopyalayıp Enterprise Manager'a yapıştırma olanağından dolayı

1
Farklı veri tipleri, Sql Enjeksiyonu, benzer parametre adları (sorunu değiştir) arasında ayrım yapmıyorsanız neden yanıt stackoverflow.com/a/265261/206730 olarak işaretlediniz ?
Kiquenet

@Kiquenet Yemin edebilirdim, bunu denedim ama izin vermedi. Şimdi çalışıyor. Bunun için teşekkür ederim.
kukla

Çalıştırılacak SQL'i doğru bir şekilde oluşturmak istiyorsanız, TdsParser.TdsExecuteRPC'ye ( github.com/Microsoft/referencesource/blob/master/System.Data/… ) bir göz atın ve biraz korkun.
Rory

Yanıtlar:


110

Mükemmel olmasa da, TSQL için devirdiğim bir şey - diğer lezzetler için kolayca değiştirilebilir ... Başka bir şey yoksa kendi geliştirmeleriniz için bir başlangıç ​​noktası verecektir :)

Bu, SSMS'de "saklı yordamı yürüt" kullanımına benzer veri türleri ve çıktı parametreleri vb. Üzerinde bir OK işi yapar. Çoğunlukla SP'leri kullandık, bu nedenle "text" komutu parametreleri vb.

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

bu, bu hatlar boyunca çıktı üretir ...

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
Aslında buradaki sorunu çözmek için iyi bir iş, sadece çaba sarf etmek için oy kullandı.
Adam Tolley

3
"ToBooleanOrDefault (false)" yönteminiz ne olurdu?
Benoittr

6
@Benoittr, ToBooleanOrDefaultburada bir uygulama görebilirsiniz : Soru # 3244850
Alexandre Marcondes

@flapper bir damla alanı veya bayt dizisi
Smith

1
Bazı küçük ayarlamalar yaptı ve tablo değeri parametreleri ekledi. Her şey GitHub ve bir .Net Standard 2.0 Nuget paketinde github.com/jphellemons/CommandAsSql Teşekkürler Flapper! Sizi ortak çalışan olarak ekleyebilir miyim?
JP Hellemons

128

Günlüğe kaydetme amaçları için, korkarım bunu yapmanın daha güzel bir yolu yoktur, ancak dizeyi kendiniz oluşturmak için:

string query = cmd.CommandText;

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

Bunu yaparsam, farklı veri türleri arasında ayrım yapmak zorunda kalacağım. Sonra parametreli sorguyu hep birlikte atlayabilir ve yürütebilirim.
kukla

2
kukla: pek değil. hazırlanmış bir beyan yürütürseniz, sql enjeksiyon saldırısı riski altındasınız. Cevap için +1.
Sunny Milenov

11
Burada bir yakaladım. Parametreler olarak "Param" ve "differentParam" varsa, "ValueParam" yerine "differentParam" ı işe yaramaz hale getirir. Param = Değer varsayarsak.
Alok

5
Soru, savunma kodlama teknikleri ile ilgili değildir, bu nedenle boş referans kontrolleri cevabın bir parçası değildir. Uygulanması gerektiği ima edilir, bu yüzden bunu yapıcı bir yorum olarak görmüyorum.
Kon

2
@Alok'un işaret ettiği benzer param isimleriyle sorunu ortadan kaldırmak için biraz daha iyi bir yaklaşım, dizedeki paramları query = Regex.Replace(query, @"\b" + p.ParameterName + @"\b", p.Value.ToString());değiştirmek için kullanmak olabilir . Bu, 'tüm kelimenin' yerini alacaktır. Evrensel bir çözüm olmayabilir ancak \ b sözcük karakteri ile sözcük olmayan bir karakter arasında bir konumu işaretlediğinden, parametre adlarınızın @ ile başlaması durumunda p.ParameterName + @"\b", sorgu dizesindeki parametreyi değiştirmek için kullanmalısınız .
stambikk

47

Yapamazsınız, çünkü herhangi bir SQL üretmez.

Parametreli sorgu ( CommandTextiçinde bir) hazırlanmış bir deyimin eşdeğeri olarak SQL Server'a gönderilir. Komutu yürüttüğünüzde, parametreler ve sorgu metni ayrı ayrı ele alınır. Hiçbir zaman tam bir SQL dizesi üretilmez.

Sahne arkasına bir göz atmak için SQL Profiler'ı kullanabilirsiniz.


6
SQL üretilir - Profiler'e bakın - bu, giriş amaçları için olmasını istediğim metindir
kpkpkp

SQL Profiler dışında (bazı MS yorumlarını doğru anladıysam, daha yeni SQL Server için kullanımdan kaldırılıyor), burada da diğer cevaba göre Etkinlik İzleyicisi'ni kullanabilir
George Birbilis

27

Daha ayrıntılı günlüğe izin vermek için dize trafosuna benzer bir komuta ihtiyacım vardı, bu yüzden bunu yazdım. Komutun çıktı parametreleri ve yapılandırılmış parametreler dahil olmak üzere yeni bir oturumda yeniden yürütülmesi için gereken metni üretecektir. Hafifçe test edilmiştir, ancak uyarı emptoru.

Misal:

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;

Üretecek:

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

Uygulama:

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

Bunun için teşekkürler, oldukça kapsamlı! :-)
Alastair Maw

Tam olarak aradığım şey, teşekkürler.
Xilmiki

Ben değişkenleri ayrı ayrı bildirmek yerine, tek bir deyimde parametreleri işlemek için sp_executesql kullanılan bir sürümü için bir başlangıç ​​noktası olarak kullandım. Bu kod gerçekten tüm sıkıcı çalışmaları hallettim ve sadece parçaları yeniden düzenlemek zorunda kaldım. Çok teşekkürler!
pettys

1
Bu SQL dize değişmezleri için bir "N" öneki gerektirmez mi? Aksi takdirde birçok "?" Alabilirsiniz. Sessizce. Kötü. (En azından SQL Server 2005 ile - daha az eski sürümlerle kontrol
etmedik

@ PaulGroke, iyi yakalama. NÖneki ekleyecek şekilde güncelledim .
Mitch

6

Ayrıca bazı parametreli sorgular veya sp's bana bir SqlException (çoğunlukla dize veya ikili veri kesilmiş olurdu) ve hata ayıklamak zor olan ifadeler (şu anda için sql-profiler desteği yok SQL Azure)

Burada tepkilerde bir sürü simüler kod görüyorum. Çözümümü ileride kullanmak üzere bir Sql-Library projesine koydum.

Jeneratör burada bulunabilir: https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.Library/SqlGenerator.cs

CommandType.Text ve CommandType.StoredProcedure öğelerini destekler

Ve nuget paketini kurarsanız , şu ifadeyle oluşturabilirsiniz:

SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters);

Çok kötü değil, en azından her bir parametrenin değerlerini listeliyor, ancak yine de değerleri doldurmuyor. En azından bunu kendim yapmak için not defteri kullanabilirim, teşekkürler!
Harvey Lin

5

SQL Server kullanıyorsanız, gerçekte yürütülen komut dizesini görüntülemek için SQL Server Profiler'ı (varsa) kullanabilirsiniz. Bu, kopyala / yapıştır test amaçlı purpuslar için yararlı olur, ancak günlüğe kaydetmek için korkarım.


3

Geç cevap, biliyorum ama ben de SQL bu yüzden bu yüzden istedim. Aşağıdakiler kısadır ve ihtiyaçlarımı karşılar.

Aşağıdakiler, SSMS'ye kopyalayabileceğiniz / yapıştırabileceğiniz SQL üretir (parametreleri düzgün değerlerle değiştirir). Daha fazla tür ekleyebilirsiniz, ancak bu, bu durumda kullandığım her şeyi karşılar.

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

Şimdi ben çalıştırmadan hemen önce SQL kaydedebilirsiniz:

LogSQL(queryCmd)
queryCmd.ExecuteNonQuery()

2

Profiler en iyi seçeneğinizdir.

İlgili hazırlama + yürütme adımları nedeniyle profilerden bir dizi ifadeyi kopyalamanız gerekebilir.


2

Aynı soruyu sordum ve bu yanıtları okuduktan sonra yanlışlıkla ortaya çıkan sorguyu almak mümkün olmadığına karar verdim. Ben hatalıydım.

Çözüm: Açık Activity Monitorde SQL Server Management Studio, uygulamanız bağlantı dizesinde kullandığı giriş kullanıcı adı, veritabanı veya uygulama adının süreçleri bölümünü daraltmak. Db yenileme çağrısı yapıldığında Activity Monitor. İşlemi gördüğünüzde, sağ tıklayın ve View Details.

Dikkat, bu meşgul bir db için uygun bir seçenek olmayabilir. Ancak bu adımları kullanarak sonucu önemli ölçüde daraltabilirsiniz.


2

Kullanılan: bölümü sineklik koduna MS SQL SMS çalıştırmak için parametre değerleri dahil tüm SQL dize döndürür benim çözümü için.

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

'Çözümünüz' çalışmıyor. \ "Ve \ n yerine" "kullanmanız gerektiğinde" "ile değiştirdiniz. Ayrıca, 9'dan fazla parametreniz varsa işe yaramaz çünkü '@ p1' yerine '@ p1' ve '@ p10' yerine her türlü çılgın sonuç gelir. Parametre listesinin kopyalanması ve geri döndürülmesi, yaptığım iş için hızlı bir düzeltmeydi.
BH

Ayrıca, kodunuz 'boş' değiştirme nedeniyle bir güncelleme komutu için çalışmaz.
BH

Aslında Flapper'ın kodu DBNull ile başa çıkmıyor, burada CommandAsSQL kütüphanesi için buna dayanan bir sorun var: github.com/jphellemons/CommandAsSql/issues/1
George Birbilis 19:01 '

2

Çözümüm:

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

Bu yöntemi benim için yazdım. Bruno Ratnieks kodunun bir kısmını kullanıyorum . Belki birisi için yararlıdır.

 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

Yalnızca bir parametrenin sonuç sorgusunda nasıl biçimlendirildiğini denetlemekse, çoğu DBMS, değişmez değerleri hiçbir şeyden sorgulamaya izin verir. Böylece:

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

Bu şekilde, tekliflerin iki katına çıkıp çıkmadığını vb. Görebilirsiniz.


1

Bu hata ayıklama konsolunda saklı bir yordam için parametre listeleri çıktısı için kullandığım şey:

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

Bu bir konsol çıktısı simlar üretecektir:

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

Bu kodu doğrudan hata ayıklamak istediğiniz herhangi bir yordamın altına yerleştirin ve bir sql profiler oturumu benzer ama C #.


1

Kon'un cevabının değiştirilmiş versiyonu, sadece benzer adlandırılmış parametrelerle kısmen çalışıyor. Dize Değiştirme işlevini kullanmanın aşağı tarafı. Bunun dışında ona çözüm konusunda tam kredi veriyorum.

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

Bu çözüm benim için çalışıyor. Belki birisi için yararlıdır. Lütfen tüm fazlalığı bahane edin.

    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

@PkExec ve @Alok'un belirttiği gibi Replace kullanımı vakaların% 100'ünde çalışmaz. Bu, yalnızca "tam kelimeyi eşleştirmek" ve veri türlerini doğru şekilde biçimlendirmek için RegExp kullanan DAL'mde kullandığım çözümdür. Böylece üretilen SQL doğrudan MySQL Workbench (veya SQLSMS, vb.) 'De test edilebilir :)

(MySQLHelper.EscapeString () işlevini kullanılan DBMS'ye göre değiştirin.)

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

Misal:

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

Üretilecek:

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

0

sql komut sorguları exec sp_executesql ile yürütülür, bu yüzden ifadeyi dize (SqlCommand uzantı yöntemi) olarak almanın başka bir yolu:

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

Bu da Mantık ile CommandAsSql kütüphanesini (yukarıdaki @ Flapper'in cevabı altındaki yorumlara bakınız) arttırmak için Saklı olmayan prosedürleri de kapsaması gerekiyordu:

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

çekme isteği şuradadır: https://github.com/jphellemons/CommandAsSql/pull/3/commits/527d696dc6055c5bcf858b9700b83dc863f04896

Regex fikri, @ stambikk'in ve EvZ'in yukarıdaki yorumlarına ve https://stackoverflow.com/a/2544661/903783 adresindeki "Güncelleme" ifadesinden bahseden "Güncelleme:" bölümüne dayanıyordu . Normal ifadenin başlangıcında sözcük sınırı algılaması için \ b yerine \ B kullanılması, p.parameterName öğesinin her zaman sözcük karakteri olmayan bir "@" ile başlamasıdır.

ParameterValueForSQL () öğesinin, tek tırnaklı dize parametre değerleri vb. sorunları ele almak için CommandAsSql kütüphanesinde tanımlanan bir uzantı yöntemi olduğunu unutmayın.


btw, diğer umut verici kod parçası github.com/jeroenpot/SqlHelper/blob/master/Source/… (bu konudaki bir cevapta belirtilmiştir) adresindedir. Birinde ya da diğerinde çalışmayan bir şey bulursanız muhtemelen SQLCommand ve SqlGenerator kodunu birleştirebilir
George Birbilis 19:18

... son yorumda SQLCommand yerine CommandAsSQL kütüphanesi demekti
George Birbilis

0

Komut metnini dönüştürürseniz:

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

Şimdi parametre olmayan komut metnini aşağıdaki gibi alabilirsiniz:

    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)

ve Sonuç artık parametre olmadan "UPDATE someTable SET Value = 'myValue' WHERE Id = 1234"


0

Saklı yordamda hata ayıklamaya yardımcı olan genişletilmiş Kon kodu :

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

İlk test durumumda, şu sonuç üretildi:

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

Muhtemelen, örneğin tarihler ve saatler için daha koşullu "..is ..." türü ödevler eklemeniz gerekecektir.


-1

Bir astar:

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

-1

Parametre komutundan parametre olmayan komuta kadar, bunu değiştirebilirsiniz

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

için

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.