我做了一些搜索,但没有找到我的问题的明确答案。
有没有办法定义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驱动程序。
答案 0 :(得分:11)
在MSDN中明确声明你不能命名参数,这是“告诉ODBC命令哪个参数是哪个”的唯一方法。
虽然文档可能会产生一些混淆:
当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调用的存储过程。在其中任何一种情况下,请使用问号(?)占位符...
答案 1 :(得分:3)
谢谢汤姆的意见和你的代码 但是,在我的测试中,代码无法正常工作 所以我写了一个更简单的(至少在我的测试工作中)解决方案,用位置参数替换命名参数(使用?而不是名称):
public static class OdbcCommandExtensions
{
public static void ConvertNamedParametersToPositionalParameters(this OdbcCommand 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 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());
}
}
答案 2 :(得分:3)
我无法使用命名参数 - 只有位置参数。 您可以添加下面所需的所有参数,但必须按顺序添加值。
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 :(得分:0)
我知道在使用Oracle Rdb ODBC时,我不能使用占位符名称而必须使用'?';我觉得非常讨厌。
答案 4 :(得分:0)
我需要编写代码来处理使用问号将命名参数转换为序数参数。我需要的是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, "?");
}
}
}
}
答案 5 :(得分:0)
这是帖子的简短解决方案:https://stackoverflow.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 "
答案 6 :(得分:0)
我更改了David Liebeherr提供的answer,以在下面列出代码。
mfeineis允许将Select @@Identity
设置为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);
}