Можно ли указывать заполнители параметров ODBC? - PullRequest
13 голосов
/ 14 июня 2011

Я провел некоторый поиск и не нашел однозначного ответа на свои вопросы.

Есть ли способ определить, какой ? в запросе 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, который я использую, является драйвером ODBC BBj.

Ответы [ 6 ]

11 голосов
/ 19 ноября 2011

В MSDN прямо указано, что вы не можете назвать параметры, которые являются единственным способом «сообщить команде ODBC, какой параметр какой».

Хотя документация может привести к путанице:

С MSDN, класс параметров OdbcParac :

Когда для CommandType задано значение Text, поставщик данных .NET Framework для ODBC не поддерживает передачу именованных параметров в оператор SQL или хранимую процедуру, вызываемую OdbcCommand. В любом из этих случаев используйте заполнитель вопросительного знака (?).

Порядок, в котором объекты OdbcParameter добавляются в OdbcParameterCollection, должен напрямую соответствовать позиции заполнителя вопросительного знака для параметра в тексте команды.

Из вышесказанного можно предположить, что, если CommandType не установлен в Text, возможно, вы можете использовать именованные параметры, но, к сожалению, вы не можете:

С MSDN, свойство OdbcCommand.CommandType :

Когда для свойства CommandType установлено значение StoredProcedure, необходимо установить для свойства CommandText полный синтаксис вызова ODBC. Затем команда выполняет эту хранимую процедуру при вызове одного из методов Execute (например, ExecuteReader или ExecuteNonQuery).

Поставщик данных .NET Framework для ODBC не поддерживает передачу именованных параметров в оператор SQL или хранимую процедуру, вызываемую OdbcCommand. В любом из этих случаев используйте заполнитель вопросительного знака (?) ...

3 голосов
/ 15 сентября 2014

Я не мог заставить его использовать именованные параметры - только позиционные параметры. Вы можете добавить все необходимые параметры, как показано ниже, но вы должны добавить значения по порядку.

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 голосов
/ 21 февраля 2014

Спасибо, Том, за твою идею и твой код.
Однако код не работал правильно в моем тесте.
Поэтому я написал более простое (и, по крайней мере, в моих тестовых работах) решение для замены именованных параметров позиционными параметрами (где вместо имени используется символ?):

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());
    }
}
0 голосов
/ 01 апреля 2016

Вот краткое решение к посту: https://stackoverflow.com/a/21925683/2935383

Я написал этот код для оболочки ODBC OpenEdge (Progress). 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 "

0 голосов
/ 19 ноября 2011

Мне нужно было написать код, который обрабатывает преобразование именованных параметров в порядковые параметры с вопросительным знаком. Мне нужно было использовать 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, "?");
            }
        }
    }
}
0 голосов
/ 14 июня 2011

Я знаю, что при использовании Oracle Rdb ODBC я не могу использовать имя заполнителя и должен использовать '?';что меня очень раздражает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...