Добавить несколько параметров в предложение IN в SQLAPI c ++ - PullRequest
0 голосов
/ 05 апреля 2019

Я использую SQLAPI для подключения к базе данных SQL-Server из кода C ++. Мне нужно выполнить простой оператор выбора с предложением IN, где строковые значения в предложении принимаются как вектор строк. Мы хотим использовать параметризованные запросы, поэтому сделали что-то вроде этого:

std::string getSQLQeury(std::vector<std::string> ids){
    std::stringstream sql;
    sql << "SELECT ID, Name, DOB FROM Employees WHERE ID IN (";
    unsigned int counter = 0;
    for each (auto id in ids)
    {
        sql << ":" << counter + 1;
        if (++counter < ids.size())
        {
            sql << ",";
        }
    }
    sql << ")";
    return sql.str();
}

А потом,

    int param_counter = 0;
    for each (auto id in ids) {
        command.Param(++param_counter).setAsString() = id.c_str();
    }

Может кто-нибудь предложить лучший способ сделать это?

1 Ответ

1 голос
/ 06 апреля 2019

Ну, я не хочу подрывать ваш основной вопрос, который я считаю «Каким хорошим способом сделать это?», Но у вас есть некоторые основные синтаксические проблемы с вашим C ++. Исходя из вашего намерения из вышеприведенного глючного кода, я понял, что цель состоит в том, чтобы создать синтезатор команд с общим запросом выбора SQL из произвольного набора идентификаторов ввода. Здорово. Я не думаю, что есть какая-то польза от создания сначала команды синтезированного шаблона с точками вставки, а затем использования их схемы замены параметров. Можно также сделать синтез сразу. Версия, корректная во время компиляции, будет выглядеть примерно так (с несколькими изменениями, чтобы сделать ее более пригодной для повторного использования) -

std::string getSQLQuery(
    const std::string& columns
  , const std::string& table
  , const std::vector<std::string>& ids
){
  // Check for empty strings/arrays here - leads to illegal SQL,
  // so error out or except on empty ids, columns, or "table".

  std::stringstream sql("SELECT ", std::ios_base::out | std::ios_base::ate);
  sql << columns << " FROM " << table << " WHERE ID IN ( ";

  // Trailing commas are not allowed in SQL, which makes synthesis a little trickier.
  // We checked for empty inputs earlier, so we have at least one ID.
  auto iter = ids.begin();
  sql << *iter++;  // add the first (and possibly only) ID
  for (; iter != ids.end(); ++iter) {  // add the rest (if any) with commas
    sql << ", " << *iter;
  }
  sql << " )";   // <- should this be " );"? Or does SQLAPI++ deal with that?
  return sql.str();  // There's a shrink-to-fit method you may want to use here.
}

Теперь вы можете просто сделать что-то вроде -

std::vector<std::string> id_array{ "1", "50", "aardvark" };
SACommand basic_command(connection, getSQLQuery("ID, Name, DOB", "Employees", id_array));
basic_command.Execute();

Это полностью пропускает второй этап замещения. Подстановка параметров SQLAPI ++ предназначена для запросов с гораздо более жестким шаблоном, но вы делаете что-то более динамичное. Вы можете представить себе, как расширить это с помощью входных массивов столбцов, чтобы избежать синтаксических ошибок в списке таблиц (как мы делаем в списке идентификаторов). Кроме того, поскольку идентификаторы часто бывают числовыми, вы можете сделать массив идентификаторов std::vector<std::uint64_t> или любой другой, который подходит для вашего конкретного приложения. Фактически, вы можете обработать оба случая с одним и тем же телом кода, сделав подпись -

template<typename T> std::string getSQLQuery(
    const std::string& columns
  , const std::string& table
  , const std::vector<T>& ids
){
   ... // rest of the implementation is the same
}

Я новый участник, но уже давно пользователь, так что пару слов о вопросах. Когда вы задаете вопрос типа «Может ли кто-нибудь предложить лучший способ сделать это?», Ответ всегда - «Да». Есть много умных людей и бесконечное количество решений для каждой проблемы высокого уровня. В будущем вы хотите заявить о проблеме, которую вы пытаетесь решить (в этом случае не сложно разобраться), и, если вы покажете решение, которое было опробовано и потерпело неудачу, вы должны сообщить подробности о том, как оно не удалось. В случае кода, который вы выдвинули, наиболее очевидная причина, по которой он потерпел неудачу, заключается в том, что он синтаксически неверен - компилятор не примет его. «для каждого» из некоторых других языков. В C ++ это что-то вроде «for (auto id: ids)». Но если вы просто пытались показать какой-то псевдокод, это говорит о том, что вы на самом деле не знаете, работает ли ваш существующий подход, потому что он не был опробован. И даже в этом случае вы должны сказать, что вам не нравится в представленном решении (например, ненужный второй шаг использования схемы замещения SQLAPI ++), и конкретно спросить, может ли кто-нибудь придумать, как удалить это. Я болтун, и я бы дал тот же ответ, но для дальнейшего использования постараюсь избежать вопроса, который сводится к следующему: «Следующий код не работает. Кто-то исправит это для меня». Просто FWIW.

...