0

我正在使用 SQLAPI 从 C++ 代码连接到 SQL-Server 数据库。我需要使用 IN 子句执行一个简单的 select 语句,其中子句中的字符串值作为字符串向量接收。我们想使用参数化查询,所以做了这样的事情:

std::string getSQLQuery(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();
    }

任何人都可以建议一个更好的方法吗?

4

1 回答 1

1

好吧,我不想削弱您的基本问题,我认为这是“什么是这样做的好方法?”,但是您的 C++ 有一些基本的语法问题。从上面的错误代码推断您的意图,我认为目标是创建一个命令合成器,其中包含来自任意输入 id 集的通用 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++ 参数替换适用于具有更严格模板的查询,但您正在做一些更动态的事情。你可以想象用列的输入数组进一步扩展它,以避免表列表中的语法错误(就像我们在 id 列表中所做的那样)。此外,由于 id 通常是数字的,因此您可以制作 id 数组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
}

我是一个新的贡献者,但一个长期的用户,所以只是关于问题的一句话。当你问这样的问题时,“有人可以建议一个更好的方法吗?”,答案总是“是的。”。那里有很多聪明人,每个高级问题都有无限的解决方案。将来,您想说明您要解决的问题(在这种情况下不难弄清楚),如果您展示了一个尝试过但失败的解决方案,您应该详细说明它是如何失败的。对于您提出的代码,它失败的最明显原因是它在语法上是错误的——编译器不会接受它。“for each”来自其他一些语言。在 C++ 中,它类似于“for (auto id : ids)”。但如果你只是想展示某种伪代码,它表明您并不真正知道您现有的方法是否有效,因为尚未尝试过。即使在这种情况下,您也应该说出您对所提出的解决方案不满意的地方(例如使用 SQLAPI++ 替换方案的不必要的第二步),并特别询问是否有人可以想出一种方法来删除它。我是个健谈者,我会给出相同的回应,但为了将来参考,尽量避免这样的问题:“以下代码已损坏。有人为我修复它。” 只是FWIW。并特别询问是否有人能想出办法来消除它。我是个健谈者,我会给出相同的回应,但为了将来参考,尽量避免这样的问题:“以下代码已损坏。有人为我修复它。” 只是FWIW。并特别询问是否有人能想出办法来消除它。我是个健谈者,我会给出相同的回应,但为了将来参考,尽量避免这样的问题:“以下代码已损坏。有人为我修复它。” 只是FWIW。

于 2019-04-06T00:28:13.917 回答