ODBC参数占位符可以命名吗?

时间:2011-06-14 01:23:34

标签: c# sql odbc

我做了一些搜索,但没有找到我的问题的明确答案。

有没有办法定义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驱动程序。

7 个答案:

答案 0 :(得分:11)

在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调用的存储过程。在其中任何一种情况下,请使用问号(?)占位符...

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