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.