可以命名 ODBC 参数占位符吗?
Posted
技术标签:
【中文标题】可以命名 ODBC 参数占位符吗?【英文标题】:Can ODBC parameter place holders be named? 【发布时间】:2011-06-14 01:23:34 【问题描述】:我进行了一些搜索,但没有找到我问题的明确答案。
有没有办法定义 SQL 查询中的哪个 ?
属于哪个参数?
例如,我需要执行以下操作:
SELECT * FROM myTable WHERE myField = @Param1 OR myField2 = @Param1
OR myField1 = @Param2 OR myField2 = @Param2
ODBC
中的同一个查询是:
SELECT * FROM myTable WHERE myField = ? or myField2 = ? or myField1 = ?
or myField2 = ?
除了为每个值加载两次参数之外,还有什么方法可以告诉 ODBC 命令哪个参数是哪个?
我怀疑没有但可以使用更有经验的 ODBC 程序员的观点。
编辑:我使用的 ODBC 驱动程序是 BBj ODBC 驱动程序。
【问题讨论】:
我的搜索出现了msdn.microsoft.com/en-us/library/ms715435%28v=vs.85%29.aspx 什么 ODBC 驱动程序?它的什么版本?这对您的问题非常重要。 您好,文章指出它只能与存储过程一起使用。我会保留这篇文章以备将来参考。 乔尔,我将它添加到理查德的答案中,但我将编辑我的 OP 以使其更清晰(它是一个 BBj ODBC 驱动程序)。谢谢! 【参考方案1】:在 MSDN 中明确声明不能命名参数,这是“告诉 ODBC 命令哪个参数是哪个”的唯一方法。
虽然文档可能会产生一些混乱:
来自MSDN, OdbcParameter Class:
当 CommandType 设置为 Text 时,用于 ODBC 的 .NET Framework 数据提供程序不支持将命名参数传递给 SQL 语句或由 OdbcCommand 调用的存储过程。在这两种情况下,请使用问号 (?) 占位符。
OdbcParameter 对象添加到 OdbcParameterCollection 的顺序必须直接对应于命令文本中参数的问号占位符的位置。
从上面看来,当 CommandType 未设置为 Text 时,也许您可以使用命名参数,但不幸的是您不能:
来自MSDN, OdbcCommand.CommandType Property:
当 CommandType 属性设置为 StoredProcedure 时,您应该将 CommandText 属性设置为完整的 ODBC 调用语法。然后,当您调用其中一种 Execute 方法(例如,ExecuteReader 或 ExecuteNonQuery)时,该命令会执行此存储过程。
用于 ODBC 的 .NET Framework 数据提供程序不支持将命名参数传递给 SQL 语句或由 OdbcCommand 调用的存储过程。在这两种情况下,请使用问号 (?) 占位符...
【讨论】:
【参考方案2】:我无法让它使用命名参数——只有位置参数。 您可以像下面一样添加所有您想要的参数,但您必须按顺序添加值。
SELECT * FROM myTable WHERE myField = ? or myField1 = ? or myField2 = ?
or myField2 = ?
myOdbcCommand.Parameters.AddWithValue("DoesNotMatter", val1); //myField
myOdbcCommand.Parameters.AddWithValue("WhatYouPutHere", val2); //myField1
myOdbcCommand.Parameters.AddWithValue("DoesNotMatter", val3); //myField2
myOdbcCommand.Parameters.AddWithValue("WhatYouPutHere", val4); //myField2
从上面可以看出,参数名称无关紧要,也不会被使用。如果您愿意,您甚至可以将它们全部命名,或者更好的是,将参数名称留空""
。
【讨论】:
【参考方案3】:感谢汤姆的想法和代码。 但是,代码在我的测试中无法正常工作。 因此,我编写了一个更简单(并且至少在我的测试工作中)的解决方案,用位置参数替换命名参数(其中使用 ? 代替名称):
public static class OdbcCommandExtensions
public static void ConvertNamedParametersToPositionalParameters(this OdbcCommand command)
//1. Find all occurrences of parameters references in the SQL statement (such as @MyParameter).
//2. For each occurrence find the corresponding parameter in the command's parameters list.
//3. Add the parameter to the newParameters list and replace the parameter reference in the SQL with a question mark (?).
//4. Replace the command's parameters list with the newParameters list.
var newParameters = new List<OdbcParameter>();
command.CommandText = Regex.Replace(command.CommandText, "(@\\w*)", match =>
var parameter = command.Parameters.OfType<OdbcParameter>().FirstOrDefault(a => a.ParameterName == match.Groups[1].Value);
if (parameter != null)
var parameterIndex = newParameters.Count;
var newParameter = command.CreateParameter();
newParameter.OdbcType = parameter.OdbcType;
newParameter.ParameterName = "@parameter" + parameterIndex.ToString();
newParameter.Value = parameter.Value;
newParameters.Add(newParameter);
return "?";
);
command.Parameters.Clear();
command.Parameters.AddRange(newParameters.ToArray());
【讨论】:
这看起来不错,尽管您可以改用IDbDataParameter
和newParameter.DbType
使其更通用。我还建议将正则表达式更改为 (@?@\\w+)
以便能够处理像 @@IDENTITY
这样的特殊情况,否则会被破坏
正则表达式不支持所有选项。例如。 @ 列中(My@Col
),逗号代表参数(SET `COL1` = @c1, `COL2` = @c2
)等【参考方案4】:
我知道在使用 Oracle Rdb ODBC 时,我不能使用占位符名称,而必须使用“?”;我觉得非常烦人。
【讨论】:
我正在使用 BBj(Java 业务基础)ODBC 驱动程序,如果有帮助的话,但我想语法不会有太大差异。【参考方案5】:我需要编写代码来处理将命名参数转换为带有问号的序数参数。我需要的是 OleDb 而不是 Odbc……但我相信如果您更改类型,这对您有用。
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Text.RegularExpressions;
namespace OleDbParameterFix
static class Program
[STAThread]
static void Main()
string connectionString = @"provider=vfpoledb;data source=data\northwind.dbc";
using (var connection = new OleDbConnection(connectionString))
using (var command = connection.CreateCommand())
command.CommandText = "select count(*) from orders where orderdate=@date or requireddate=@date or shippeddate=@date";
command.Parameters.Add("date", new DateTime(1996, 7, 11));
connection.Open();
OleDbParameterRewritter.Rewrite(command);
var count = command.ExecuteScalar();
connection.Close();
public class OleDbParameterRewritter
public static void Rewrite(OleDbCommand command)
HandleMultipleParameterReferences(command);
ReplaceParameterNamesWithQuestionMark(command);
private static void HandleMultipleParameterReferences(OleDbCommand command)
var parameterMatches = command.Parameters
.Cast<OleDbParameter>()
.Select(x => Regex.Matches(command.CommandText, "@" + x.ParameterName))
.ToList();
// Check to see if any of the parameters are listed multiple times in the command text.
if (parameterMatches.Any(x => x.Count > 1))
var newParameters = new List<OleDbParameter>();
// order by descending to make the parameter name replacing easy
var matches = parameterMatches.SelectMany(x => x.Cast<Match>())
.OrderByDescending(x => x.Index);
foreach (Match match in matches)
// Substring removed the @ prefix.
var parameterName = match.Value.Substring(1);
// Add index to the name to make the parameter name unique.
var newParameterName = parameterName + "_" + match.Index;
var newParameter = (OleDbParameter)((ICloneable)command.Parameters[parameterName]).Clone();
newParameter.ParameterName = newParameterName;
newParameters.Add(newParameter);
// Replace the old parameter name with the new parameter name.
command.CommandText = command.CommandText.Substring(0, match.Index)
+ "@" + newParameterName
+ command.CommandText.Substring(match.Index + match.Length);
// The parameters were added to the list in the reverse order to make parameter name replacing easy.
newParameters.Reverse();
command.Parameters.Clear();
newParameters.ForEach(x => command.Parameters.Add(x));
private static void ReplaceParameterNamesWithQuestionMark(OleDbCommand command)
for (int index = command.Parameters.Count - 1; index >= 0; index--)
var p = command.Parameters[index];
command.CommandText = command.CommandText.Replace("@" + p.ParameterName, "?");
【讨论】:
【参考方案6】:这是帖子的简短解决方案:https://***.com/a/21925683/2935383
我为 OpenEdge (Progress) ODBC 包装器编写了这段代码。 DatabaseAdapter-class 就是这个包装器,这里就不展示了。
string _convertSql( string queryString, List<DatabaseAdapter.Parameter> parameters,
ref List<System.Data.Odbc.OdbcParameter> odbcParameters )
List<ParamSorter> sorter = new List<ParamSorter>();
foreach (DatabaseAdapter.Parameters item in parameters)
string parameterName = item.ParameterName;
int indexSpace = queryString.IndexOf(paramName + " "); // 0
int indexComma = queryString.IndexOf(paramName + ","); // 1
if (indexSpace > -1)
sorter.Add(new ParamSorter() p = item, index = indexSpace, type = 0 );
else
sorter.Add(new ParamSorter() p = item, index = indexComma, type = 1 );
odbcParameters = new List<System.Data.Odbc.OdbcParameter>();
foreach (ParamSorter item in sorter.OrderBy(x => x.index))
if (item.type == 0) //SPACE
queryString = queryString.Replace(item.p.ParameterName + " ", "? ");
else //COMMA
queryString = queryString.Replace(item.p.ParameterName + ",", "?,");
odbcParameters.Add(
new System.Data.Odbc.OdbcParameter(item.p.ParameterName, item.p.Value));
用于排序的实用程序类
class ParamSorter
public DatabaseAdapter.Parameters p;
public int index;
public int type;
如果命名参数是字符串中的最后一个 - 你必须添加一个空格。
例如"SELECT * FROM tab WHERE col = @mycol"
必须"SELECT * FROM tab WHERE col = @mycol "
【讨论】:
【参考方案7】:我将 David Liebeherr 提供的 answer 更改为下面的代码。
它允许 Select @@Identity
被 mfeineis 设置为 mentioned。
public static IDbCommand ReplaceCommndTextAndParameters(this IDbCommand command, string commandText, List<IDbDataParameter> parameters)
command.CommandText = commandText;
command.Parameters.Clear();
foreach (var p in parameters)
command.Parameters.Add(p);
return command;
public static IDbCommand ConvertNamedParametersToPositionalParameters(this IDbCommand command)
var newCommand = command.GetConvertNamedParametersToPositionalParameters();
return command.ReplaceCommndTextAndParameters(newCommand.CommandText, newCommand.Parameters);
public static (string CommandText, List<IDbDataParameter> Parameters) GetConvertNamedParametersToPositionalParameters(this IDbCommand command)
//1. Find all occurrences parameters references in the SQL statement (such as @MyParameter).
//2. Find the corresponding parameter in the command's parameters list.
//3. Add the found parameter to the newParameters list and replace the parameter reference in the SQL with a question mark (?).
//4. Replace the command's parameters list with the newParameters list.
var oldParameters = command.Parameters;
var oldCommandText = command.CommandText;
var newParameters = new List<IDbDataParameter>();
var newCommandText = oldCommandText;
var paramNames = oldCommandText.Replace("@@", "??").Split('@').Select(x => x.Split(new[] ' ', ')', ';', '\r', '\n' ).FirstOrDefault().Trim()).ToList().Skip(1);
foreach (var p in paramNames)
newCommandText = newCommandText.Replace("@" + p, "?");
var parameter = oldParameters.OfType<IDbDataParameter>().FirstOrDefault(a => a.ParameterName == p);
if (parameter != null)
parameter.ParameterName = $"parameter.ParameterName_newParameters.Count";
newParameters.Add(parameter);
return (newCommandText, newParameters);
【讨论】:
以上是关于可以命名 ODBC 参数占位符吗?的主要内容,如果未能解决你的问题,请参考以下文章