【问题标题】:Convert SqlCommand to T-SQL command将 SqlCommand 转换为 T-SQL 命令
【发布时间】:2010-10-16 00:31:29
【问题描述】:

我有一个带参数的SqlCommand。由于一些外部要求(参见 PS),我需要 一个 T-SQL 字符串,即,我需要将 ... @parameter ... 替换为 ... the-value-of-@parameter-encoded-correctly ...(例如 O'Brien --> 'O''Brien', 02 Mar 2010 --> '20100302', 3.5 --> 3.5)。

我知道我可以很容易地自制这样的解决方案,但正确的转义是棘手的(确保你得到正确的日期和数字格式,注意字符串中的引号等),我想我'不是唯一需要这个的人,所以我的问题是:

是否存在将带参数的 SqlCommand 转换为单个 T-SQL 语句且参数嵌入并正确转义的现有解决方案?

PS:我们需要这样做,因为我们需要将SqlCommand 用作 MS Access 报告的数据源...而且 Access 中的“pass-through querydefs”不支持参数化 SQL。

PPS:我知道有similarquestions,但他们都假设 SQL Server 以某种方式进行了这种转换(@parameter --> 一些表示值的转义字符串),所以答案(“SQL Server不这样做") 不适用于此处。我知道 SQL Server 中没有发生这种转换。

【问题讨论】:

  • 我不想否定,但正如其他问题已经澄清 - 正如你自己提到的 - 你正在寻找的 TSQL 字符串无论如何都不会生成。因此,除了推出自己的应用程序或以某种方式修改您的应用程序之外,我不确定您希望得到什么答案?
  • @Pondlife:我希望得到这样的答案:“是的,我以前遇到过这个问题,我已经在 C#/VB {link to source} 中编写并发布了一个例程,它逃脱了根据在 {link to MSDN} 找到的 SQL Server 转义规范,所有类型的值都正确。"

标签: c# sql-server vb.net ado.net


【解决方案1】:

我认为写出带有参数的 SQL 查询并在您的主要 SELECT 查询之前有几行 DECLARE @p1 type; 和 SET @p1 = 'value'; 并保持 SqlCommand 实例中的原始查询文本不变会更清楚。如果您愿意,我有一些示例代码可以执行此操作。

如果您真的坚持只在查询文本中进行参数值替换,那么请为自己找到一个特定于您正在与之交谈的服务器的 T-SQL 语法,解析查询直到找到参数引用,然后替换根据 T-SQL 语法的字符串转义规则,将其与文本结合起来。

编辑:

既然我是个了不起的人,我将带您开始格式化 SQL 参数以通过适当的转义输出输出。这是非常不完整的,因为它不能处理人类和 SQL Server 已知的每一种数据库类型,但它为我完成了工作,因为它处理了我最常用的类型。正如我在评论中所说,简单地将单引号字符连续转义为两个单引号字符似乎足以进行正确的字符串值转义。您必须反复检查各种 SQL Server 版本使用的 T-SQL 语法,以确保这足够好。

private static string FormatSqlValue(System.Data.Common.DbParameter prm)
{
    if (prm.Value == DBNull.Value) return "NULL";
    switch (prm.DbType)
    {
        case System.Data.DbType.Int32: return (prm.Value.ToString());
        case System.Data.DbType.String: return String.Format("'{0}'", ScrubSqlString((string)prm.Value));
        case System.Data.DbType.AnsiString: return String.Format("'{0}'", ScrubSqlString((string)prm.Value));
        case System.Data.DbType.Boolean: return ((bool)prm.Value ? "1" : "0");
        case System.Data.DbType.DateTime: return String.Format("'{0}'", prm.Value.ToString());
        case System.Data.DbType.DateTime2: return String.Format("'{0}'", prm.Value.ToString());
        case System.Data.DbType.Decimal: return (prm.Value.ToString());
        case System.Data.DbType.Guid: return String.Format("'{0}'", prm.Value.ToString());
        case System.Data.DbType.Double: return (prm.Value.ToString());
        case System.Data.DbType.Byte: return (prm.Value.ToString());
        // TODO: more conversions.
        default: return (prm.DbType.ToString());
    }
}

private static string FormatSqlValue(Type type, object value)
{
    if (value == null)
        return "NULL";
    // Handle Nullable<T> types:
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        // If the Nullabe<T> value has been found to have !HasValue, return "NULL":
        if (!(bool)type.GetProperty("HasValue").GetValue(value, null))
            return "NULL";
        // Try our best to format the underlying non-nullable value now:
        return FormatSqlValue(type.GetGenericArguments()[0], type.GetProperty("Value").GetValue(value, null));
    }
    if (type == typeof(Int32)) return value.ToString();
    if (type == typeof(String)) return String.Format("'{0}'", ScrubSqlString((string)value));
    if (type == typeof(Boolean)) return ((bool)value ? "1" : "0");
    if (type == typeof(DateTime)) return String.Format("'{0}'", value.ToString());
    if (type == typeof(Decimal)) return (value.ToString());
    if (type == typeof(Guid)) return String.Format("'{0}'", value.ToString());
    if (type == typeof(Double)) return (value.ToString());
    if (type == typeof(Byte)) return (value.ToString());
    // TODO: complete the mapping...
    return value.ToString();
}

private static string ScrubSqlString(string value)
{
    StringBuilder sb = new StringBuilder();
    int i = 0;
    while (i < value.Length)
    {
        if (value[i] == '\'')
        {
            sb.Append("\'\'");
            ++i;
        }
        else
        {
            sb.Append(value[i]);
            ++i;
        }
    }
    return sb.ToString();
}

private static string FormatSqlParameter(System.Data.Common.DbParameter prm)
{
    StringBuilder sbDecl = new StringBuilder();
    sbDecl.Append(prm.ParameterName);
    sbDecl.Append(' ');
    switch (prm.DbType)
    {
        case System.Data.DbType.Int32: sbDecl.Append("int"); break;
        // SQL does not like defining nvarchar(0).
        case System.Data.DbType.String: sbDecl.AppendFormat("nvarchar({0})", prm.Size == -1 ? "max" : prm.Size == 0 ? "1" : prm.Size.ToString()); break;
        // SQL does not like defining varchar(0).
        case System.Data.DbType.AnsiString: sbDecl.AppendFormat("varchar({0})", prm.Size == -1 ? "max" : prm.Size == 0 ? "1" : prm.Size.ToString()); break;
        case System.Data.DbType.Boolean: sbDecl.Append("bit"); break;
        case System.Data.DbType.DateTime: sbDecl.Append("datetime"); break;
        case System.Data.DbType.DateTime2: sbDecl.Append("datetime2"); break;
        case System.Data.DbType.Decimal: sbDecl.Append("decimal"); break;  // FIXME: no precision info in DbParameter!
        case System.Data.DbType.Guid: sbDecl.Append("uniqueidentifier"); break;
        case System.Data.DbType.Double: sbDecl.Append("double"); break;
        case System.Data.DbType.Byte: sbDecl.Append("tinyint"); break;
        // TODO: more conversions.
        default: sbDecl.Append(prm.DbType.ToString()); break;
    }
    return sbDecl.ToString();
}

【讨论】:

  • +1,谢谢,这是一个不错的方法。但是,原来的问题仍然存在,因为SET @p1 = 'value' 需要与WHERE myfield = 'value' 相同的转义类型。所以 both 都需要“根据语法规则转义”。您是否有指向 SQL Server 的“转义规则”的链接? (对于 SO 上的此类“转义规则”的问题会立即以“仅使用参数化查询”来回答,这在这种情况下并不是很有帮助...)
  • 我发帖后才意识到 :) 对不起。我只做了基本的字符串转义规则,这些规则“足够好”用于我的显示目的。如果你只是掩盖转义引号字符,我认为你已经准备好了。
  • @Heinzi:我用我为我的 SQL 审计工具开发的代码更新了答案,该工具向人类用户显示 SQL 代码,而不是由 T-SQL 引擎解析。如果可能的话,您会发现 TODO 和 FIXME 指出了哪些地方可以改进。希望有帮助!
  • @James:谢谢,这绝对是一个不错的起点!显然会破坏的一件事是在没有文化说明符的情况下使用 ToString:在我的德语系统上,这将返回 3,5 用于双精度和 '31.12.2010' 用于日期时间,这两者都会导致 SQL Server窒息(顺便说一句,正确的输入是'20101231',如果我没记错的话,SQL Server 将始终接受独立于任何本地化设置的输入)。 ...
  • @James: ...实际上,这是我提出问题的主要原因:既然有这么多“小问题”需要考虑,我想知道是否有人已经写过“生产质量" 这个常见问题的库代码。 (但不要误会我的意思:我仍然感谢您的帮助!)
猜你喜欢
  • 2019-05-16
  • 2011-02-02
  • 1970-01-01
  • 2019-06-12
  • 1970-01-01
  • 1970-01-01
  • 2013-11-08
  • 1970-01-01
  • 2011-02-28
相关资源
最近更新 更多