0

我们需要在工作场所定期解决的一个问题是如何根据用户提供的表/列名构建 sql 语句。我要解决的问题是列名之间的逗号。

一种技术看起来像这样。

selectSql  = "SELECT ";

for (z = 0; z < columns.size(); z++)
{
    selectSql += columns[z]._name;
    selectSql += ", "; 
}

selectSql = selectSql(0, selectSql.len() - 2);

selectSql += "FROM some-table";

另一种技术看起来像这样

selectSql  = "SELECT ";

for (z = 0; z < columns.size(); z++)
{
    selectSql += columns[z]._name;
    if (z < columns.size() - 1) 
        selectSql += ", "; 
}

selectSql += "FROM some-table";

我对这些实现中的任何一个都不是特别着迷。

我很高兴听到有关解决此问题的其他方法的想法,着眼于使代码更易于阅读/理解/维护。

有哪些替代技术可用?

4

8 回答 8

5

在您的情况下,假设至少有一列可能是安全的,否则进行选择是没有意义的。在这种情况下,您可以这样做:

selectSql  = "SELECT ";
selectSql += columns[0]._name;

for (z = 1; z < columns.size(); z++) {
   selectSql += ", ";
   selectSql += columns[z]._name;
}

selectSql += " FROM some-table";
于 2008-11-29T02:37:04.387 回答
3

您可以通过编写一个函数对象并使用类似 strager 提出的方法(尽管他的实现不是 C++),而不是每次都再次应用解决方法,从而一劳永逸地解决问题:

struct join {
    std::string sep;
    join(std::string const& sep): sep(sep) { }

    template<typename Column>
    std::string operator()(Column const& a, Column const& b) const {
        return a._name + sep + b._name;
    }
};

由于我不知道您的列类型,因此我将其保留为模板。现在,每当您想构建查询时,只需执行

std::string query = std::accumulate(cols.begin(), cols.end(), 
    std::string("SELECT "), join(", ")) + " FROM some-table;";
于 2008-11-29T19:09:26.163 回答
2

我们不打扰删除尾随的逗号。
这是因为您可以选择一个常量并且 SQL 仍然有效。

SELECT A FROM T

-- Is the same as 

SELECT A,1 FROM T

-- Apart from there is an extra column named 1 where each value is 1

因此使用 STL 使其紧凑:

#include <sstream>
#include <iterator>
#include <algorithm>

    std::stringstream           select;

    // Build select statement.
    select << "SELECT ";
    std::copy(col.begin(),col.end(),std::ostream_iterator<std::string>(select," , "));
    select << " 1 FROM TABLE PLOP";
于 2008-11-29T03:09:16.100 回答
1

我建立语句的方式通常是:

pad = ""
stmt = "SELECT "

for (i = 0; i < number; i++)
{
    stmt += pad + item[i]
    pad = ", "
}

这是相对干净的 - 它重新分配以填充每次迭代,但这是微不足道的。我对此使用了许多微不足道的变体,但它是我所知道的最干净的机制。

当然,也会有其他人的答案可以借鉴...

于 2008-11-29T01:42:59.300 回答
1

它不必那么复杂。

string sql = "SELECT " + join(cols.begin(), cols.end(), ", ") + " FROM some_table";

在哪里

template <typename I>
string join(I begin, I end, const string& sep){
   ostringstream out;
   for(; begin != end; ++begin){
      out << *begin;
      if(begin+1 != end) out << sep;
   }
   return out.str();
}
于 2008-11-29T19:55:54.867 回答
1

不赘述这一点,而是看一下 boost::algorithm::join()。这是一个示例,以防您认为他们的文档对于文字来说太密集了:

std::string
build_sql(std::vector<std::string> const& colNames,
          std::string const& tableName)
{
    std::ostringstream sql;
    sql << "SELECT "
        << boost::algorithm::join(colNames, std::string(","))
        << " FROM " << tableName;
    return sql.str();
}

如有疑问,请查看 Boost.org。他们通常已经有了解决大多数类似问题的方法。

于 2008-11-29T21:21:35.507 回答
0

我建议构建一个通用的连接函数来做到这一点。您可以使用例如累积算法来连接列。

编辑:litb 的实现;它不那么天真。

// Untested
#include <numeric>

template<std::string separator>
struct JoinColumns {
    std::string operator()(Column a, Column b) {
        return a._name + separator + b._name;
    }

    // Too lazy to come up with a better name
    std::string inArray(T array) {
        stl::accumulate(array.begin(), array.end(), std::string(), *this);
    }
};

selectSql += stl::accumulate(columns.begin(), columns.end(), std::string(), JoinColumns<", ">());
// or
selectSql += JoinColumns<", ">().inArray(columns);

当然,您可以通过使用更好的包装器来获得更简洁的语法。

于 2008-11-29T01:44:12.277 回答
0
for (z = 0; z < columns.size(); z++)
{
    if( z != 0 )
        selectSql += ", "; 
    selectSql += columns[z]._name;
}
于 2008-11-29T02:19:45.593 回答