SqlCommand 개체에서 생성 된 SQL 문을 가져 오시겠습니까?


186

다음 코드가 있습니다.

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

최종 SQL 문을 String으로 가져올 수있는 방법이 있는지 궁금합니다.

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

내가 왜 이것을 할 것인지 궁금해하는 사람이 있다면 :

  • 로깅 (실패) 문
  • 테스트 목적으로 Enterprise Manager에 복사하여 붙여 넣을 수있는 가능성

1
다른 데이터 유형, Sql Injection, 비슷한 매개 변수 이름 (문제 대체)을 구별하지 못하는 경우 왜 stackoverflow.com/a/265261/206730 이라고 답 했는지?
Kiquenet

@ Kiquenet 나는 맹세 할 수 있었지만, 그것을 시도했지만 그것을 보내지 못했습니다. 이제 작동합니다. 감사합니다.
더미

실행될 SQL을 정확하게 생성하려면 TdsParser.TdsExecuteRPC ( github.com/Microsoft/referencesource/blob/master/System.Data/… )를 보고 조금 두려워하십시오.
Rory

답변:


110

완벽하지는 않지만 여기에 TSQL에 대한 문제가 있습니다. 다른 맛을 쉽게 조정할 수 있습니다.

이것은 SSMS에서 "execute 저장 프로 시저"를 사용하는 것과 유사한 데이터 형식 및 출력 매개 변수에 대해 OK 작업을 수행합니다. 우리는 주로 SP를 사용했기 때문에 "text"명령은 매개 변수 등을 설명하지 않습니다.

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

이것은이 라인을 따라 출력을 생성합니다 ...

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
실제로 여기에서 문제를 해결하려고 노력하는 것은 좋은 일이었습니다.
Adam Tolley

3
"ToBooleanOrDefault (false)"방법은 무엇입니까?
Benoittr

6
@Benoittr, 당신은 ToBooleanOrDefault여기에 구현을 볼 수 있습니다 : 질문 # 3244850
Alexandre Marcondes

@flapper blob 필드 또는 바이트 배열의 내용
Smith

1
약간의 조정과 테이블 값 매개 변수를 추가했습니다. 모두 GitHub와 .Net Standard 2.0 Nuget 패키지 github.com/jphellemons/CommandAsSql에 있습니다. 플래퍼 감사합니다! 공동 작업자로 추가 할 수 있습니까?
JP Hellemons

128

로깅 목적으로,이 작업을 수행하는 더 좋은 방법은 없지만 문자열을 직접 구성하는 것이 두렵습니다.

string query = cmd.CommandText;

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

그렇게하면 다른 데이터 유형을 구별해야합니다. 그런 다음 매개 변수가있는 쿼리를 모두 건너 뛰고 실행할 수 있습니다.
더미

2
더미 : 실제로는 아닙니다. 준비된 명령문을 실행하면 SQL 삽입 공격의 위험이 있습니다. 답변은 +1입니다.
Sunny Milenov

11
여기에 문제가 있습니다. "Param"및 "differentParam"을 매개 변수로 사용하면 differentParam이 "ValueParam"으로 바뀌면서 쓸모가 없게됩니다. Param = Value라고 가정합니다.
Alok

5
이 질문은 방어 적 인 코딩 기술을 다루지 않으므로 null 참조 확인은 답의 일부가 아닙니다. 그것이 구현되어야한다는 사실이 암시되어 있으므로 이것을 건설적인 의견으로 보지 않습니다.
Kon

2
@Alok이 지적한 유사한 매개 변수 이름의 문제를 제거하는 약간 더 나은 방법 query = Regex.Replace(query, @"\b" + p.ParameterName + @"\b", p.Value.ToString());은 문자열의 매개 변수를 대체하는 데 사용할 수 있습니다 . 이것은 '전체 단어'를 대체합니다. \ b가 단어 문자와 단어가 아닌 문자 사이의 위치를 ​​표시하므로 매개 변수 이름이 @로 시작하는 p.ParameterName + @"\b"경우 쿼리 문자열에서 param을 바꾸는 데 사용해야 합니다.
stambikk

47

SQL을 생성하지 않으므로 수행 할 수 없습니다.

매개 변수화 된 쿼리 (에있는 쿼리 CommandText)는 준비된 명령문과 동등한 SQL Server로 전송됩니다. 명령을 실행하면 매개 변수와 조회 텍스트가 별도로 처리됩니다. 완전한 SQL 문자열이 생성되지 않습니다.

SQL 프로파일 러를 사용하여 배후를 살펴볼 수 있습니다.


6
SQL이 생성됩니다-프로파일 러를 살펴보십시오
이것이

떨어져 SQL도 여기에 다른 대답에 따라 활동 모니터를 사용할 수 있습니다 (I 올바르게 일부 MS의 의견을 이해하면 새로운 SQL Server에 대해 사용되지됩니다) 프로파일에서
조지 Birbilis

27

더 자세한 로깅을 허용하기 위해 문자열 변환기와 비슷한 명령이 필요했기 때문에이 명령을 작성했습니다. 출력 매개 변수 및 구조화 된 매개 변수를 포함하여 새 세션에서 명령을 다시 실행하는 데 필요한 텍스트를 생성합니다. 가볍게 테스트되었지만주의를 기울여야합니다.

예:

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;

생산할 것이다 :

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

이행:

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

이것에 감사합니다, 그것은 매우 포괄적입니다! :-)
Alastair Maw

정확히 내가 찾던 것, 고마워
Xilmiki

나는 이것을 sp_executesql을 사용하여 변수를 개별적으로 선언하는 대신 단일 명령문으로 매개 변수를 처리하는 버전의 시작점으로 사용했습니다. 이 코드는 실제로 모든 지루한 작업을 처리했으며 조각을 다시 정렬해야했습니다. 큰 감사를 드린다!
pettys

1
SQL 문자열 리터럴에 "N"접두사가 필요하지 않습니까? 그렇지 않으면 많은 "?"가 표시 될 수 있습니다. 아무 말 않고. 나쁜. (적어도 SQL Server 2005에서는 덜 오래된 버전으로 확인하지 않았습니다.)
Paul Groke

@PaulGroke, 잘 잡았습니다. N접두사 를 포함하도록 업데이트되었습니다 .
Mitch

6

또한 일부 매개 변수가있는 쿼리 또는 sp가 SqlException (주로 문자열 또는 이진 데이터가 잘릴 수 있음)을 제공하고 디버깅하기 어려운 명령문 (현재 알고있는 한 sql-profiler 지원이없는 경우) SQL Azure)

반응에 많은 유사한 코드가 있습니다. 나중에 사용하기 위해 솔루션을 Sql-Library 프로젝트에 넣었습니다.

생성기는 여기에서 사용할 수 있습니다 : https://github.com/jeroenpot/SqlHelper/blob/master/Source/Mirabeau.MsSql.Library/SqlGenerator.cs

CommandType.Text 및 CommandType.StoredProcedure를 모두 지원합니다.

그리고 너겟 패키지 를 설치하면 다음 문장으로 생성 할 수 있습니다.

SqlDebugHelper.CreateExecutableSqlStatement(sql, parameters);

나쁘지는 않지만 적어도 각 매개 변수의 값을 나열하지만 실제로 값을 채우지는 않습니다. 적어도 메모장을 사용하여 직접 할 수 있습니다. 감사합니다!
Harvey Lin

5

SQL Server를 사용하는 경우 SQL Server 프로파일 러 (있는 경우)를 사용하여 실제로 실행되는 명령 문자열을 볼 수 있습니다. 복사 / 붙여 넣기 테스트 purpuses에는 유용하지만 로깅에는 유용하지 않습니다.


3

늦은 대답, 나는 알고 있지만 SQL을 로그 할 수 있도록 이것을 원했습니다. 다음은 짧고 내 요구를 충족시킵니다.

다음은 SSMS에서 복사 / 붙여 넣기 할 수있는 SQL을 생성합니다 (매개 변수가 값으로 바뀝니다). 더 많은 유형을 추가 할 수는 있지만이 경우에 사용하는 모든 것을 충족시킵니다.

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

이제 SQL을 실행하기 직전에 로그를 기록 할 수 있습니다.

LogSQL(queryCmd)
queryCmd.ExecuteNonQuery()

2

프로파일 러는 최선의 선택입니다.

준비 + 실행 단계로 인해 프로파일 러에서 명령문 세트를 복사해야 할 수도 있습니다.


2

나는 똑같은 정확한 질문을 했고이 응답을 읽은 후 정확한 결과 쿼리를 얻는 것이 불가능하다고 결정했습니다. 내가 틀렸어.

해결 방법 : 열기 Activity Monitor의는 SQL Server Management Studio, 응용 프로그램이 연결 문자열에 사용하고있는 로그인 이름, 데이터베이스 또는 응용 프로그램 이름에 프로세스 섹션을 축소. DB 새로 고침을 호출 할 때 Activity Monitor. 프로세스가 보이면 마우스 오른쪽 단추로 클릭하고을 클릭하십시오 View Details.

이는 사용중인 DB에 대해 실행 가능한 옵션이 아닐 수 있습니다. 그러나이 단계를 사용하여 결과를 상당히 좁힐 수 있어야합니다.


2

내 솔루션에 대한 Flapper 코드 일부를 사용 하여 MS SQL SMS에서 실행할 매개 변수 값을 포함한 전체 SQL 문자열을 반환합니다.

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

귀하의 '솔루션'이 작동하지 않습니다. ""를 사용해야했을 때 \ r 및 \ n을 ""로 바꿨습니다. 또한 '@ p1'을 바꾸면 '@ p1'과 '@ p10'이 모든 종류의 미친 결과로 바뀌므로 9 개 이상의 매개 변수가 있으면 작동하지 않습니다. 매개 변수 목록을 복사하고 되 돌리는 것은 내가하고있는 일에 대한 빠른 수정이었습니다.
BH

또한 'is null'교체로 인해 업데이트 명령에 코드가 작동하지 않습니다.
BH

실제로 플래퍼의 코드는 DBNull을 처리하지 않으므로 여기에 기반한 CommandAsSQL 라이브러리에 대한 문제가 있습니다. github.com/jphellemons/CommandAsSql/issues/1
George Birbilis

2

내 솔루션 :

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

나는이 방법을 썼다. Bruno Ratnieks 코드 일부를 사용 합니다. 아마도 누군가에게 유용 할 것입니다.

 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

결과 쿼리에서 매개 변수의 형식을 검사하는 것만 확인하는 경우 대부분의 DBMS는 리터럴 쿼리를 전혀 허용하지 않습니다. 그러므로:

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

그렇게하면 따옴표가 두 배가되는지 확인할 수 있습니다.


1

스토어드 프로 시저의 매개 변수 목록을 디버그 콘솔에 출력하는 데 사용합니다.

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

이것은 다음과 비슷한 콘솔 출력을 생성합니다.

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

디버깅하려는 프로 시저 바로 아래에이 코드를 배치하고 SQL 프로파일 러 세션과 유사하지만 C #으로 작성합니다.


1

Kon의 답변의 수정 된 버전은 부분적으로 유사한 명명 된 매개 변수로만 작동하기 때문에 수정 되었습니다. String Replace 기능 사용의 단점. 그 외에는 그에게 솔루션에 대한 완전한 신용을 부여합니다.

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

이 솔루션은 지금 저에게 효과적입니다. 아마도 누군가에게 유용 할 것입니다. 모든 중복성을 변명하십시오.

    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 및 @Alok에서 언급했듯이 100 %의 경우 Replace 사용이 작동하지 않습니다. 이것은 DEX에서 RegExp를 사용하여 "단어 전체"만 일치시키고 데이터 형식을 올바르게 형식화하는 솔루션입니다. 따라서 생성 된 SQL은 MySQL Workbench (또는 SQLSMS 등)에서 직접 테스트 할 수 있습니다 :)

(사용 된 DBMS에 따라 MySQLHelper.EscapeString () 함수를 대체하십시오.)

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

예:

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

생성됩니다 :

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

0

sql 명령 쿼리는 exec sp_executesql로 실행되므로 명령문을 문자열로 가져 오는 또 다른 방법이 있습니다 (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

저장되지 않은 프로시 저도 다루어야 하므로이 논리로 CommandAsSql 라이브러리를 확장했습니다 (위의 @Flapper 답변 아래 주석 참조).

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

풀 요청은 https://github.com/jphellemons/CommandAsSql/pull/3/commits/527d696dc6055c5bcf858b9700b83dc863f04896입니다.

정규식 아이디어는 위의 @ stambikk 's 및 EvZ의 의견과 "negative look-behind assertion"을 언급 하는 https://stackoverflow.com/a/2544661/903783 의 "Update :"섹션 을 기반으로합니다. 정규식 시작시 단어 경계 감지에 \ b 대신 \ B를 사용하면 p.parameterName은 항상 단어 문자가 아닌 "@"로 시작하기 때문입니다.

ParameterValueForSQL ()은 작은 따옴표 문자열 매개 변수 값 등과 같은 문제를 처리하기 위해 CommandAsSql 라이브러리에 정의 된 확장 메소드입니다.


btw, 다른 유망한 코드는 github.com/jeroenpot/SqlHelper/blob/master/Source/… (이 스레드의 답변에서 언급)에 있습니다. 당신이 뭔가 하나 또는 다른에서 작동하지 않는 찾으면 아마하는 SqlCommand와 SqlGenerator에서 코드를 병합 할 수
조지 Birbilis

... 마지막 주석에서 SQLCommand 대신 CommandAsSQL 라이브러리
George Birbilis

0

명령 텍스트를 변환 할 경우 :

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

이제 다음과 같이 비 매개 변수 명령 텍스트를 얻을 수 있습니다.

    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)

그리고 결과는 더 이상 매개 변수없이 "UPDATE someTable SET Value = 'myValue'WHERE Id = 1234"입니다


0

저장 프로 시저를 디버깅하는 데 도움이되는 확장 된 Kon 코드 :

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

첫 번째 테스트 사례에서 다음을 생성했습니다.

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

날짜 및 시간과 같은 조건부 "..is ..."유형 지정을 추가해야 할 수도 있습니다.



-1

매개 변수 명령에서 비 매개 변수 명령으로이 항목을 변경할 수 있습니다

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

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.