【问题标题】:using a vector of column names, to generate a sql statement使用列名向量生成 sql 语句
【发布时间】:2008-11-29 01:29:14
【问题描述】:

我们需要在工作场所定期解决的一个问题是如何根据用户提供的表/列名称构建 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";

我对这两种实现都不是特别着迷。

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

有哪些替代技术可用?

【问题讨论】:

    标签: c++ sql idioms


    【解决方案1】:

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

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

    【讨论】:

    • 看起来也很干净,没有经过测试。
    • for 循环隐含了一个测试:)
    • 我当然指的是循环体内的额外测试,或循环外的额外处理,以删除最后的逗号:)
    【解决方案2】:

    您可以通过编写一个函数对象并使用类似 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;";
    

    【讨论】:

      【解决方案3】:

      我们不会费心删除结尾的逗号。
      这是因为您可以选择一个常量,并且 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";
      

      【讨论】:

      • 好主意。 “我怎么没想到呢?”
      • 它很好地简化了事情。人们可能会滥用该技术在插入语句中处理列和值。
      • 您现在从服务器返回的每行数据都带回了额外的数据,这意味着通过网络的额外流量。多花十亿分之一秒来整理您的列/逗号,而不是受到将乘以返回的行(可能很大)的命中。
      • (contd) 忘记包含...这也可能意味着在服务器和客户端上使用了额外的内存。
      • @Tom H:是的,有一笔交易,就像所有 CS 决策一样。您必须衡量时间/空间的增加,看看是否值得降低代码的复杂性(从而更容易维护)。
      【解决方案4】:

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

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

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

      当然,也有别人的答案可以借鉴……

      【讨论】:

      • 我在我的快速脏代码中使用了类似的方法,除了我使用 bool 来检查是否需要打印逗号。
      • 我要说的是你最终会用逗号结尾。然后我看到了你在那里做了什么。我想说这段代码有点“棘手”,很难维护。
      • 此外,您最终会为 'pad' 进行冗余分配,如果这是一个高性能循环,那将是一种浪费。
      • @John Dibling:取决于语言。对我来说,最常见的情况是,在 C 中,pad 是一个 const char 指针,并且赋值是微不足道的。 SQL 语句的执行时间要比创建时间长得多。
      【解决方案5】:

      没必要这么复杂。

      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();
      }
      

      【讨论】:

        【解决方案6】:

        不赘述,看一下 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。他们通常已经有了解决大多数此类问题的方法。

        【讨论】:

          【解决方案7】:
          for (z = 0; z < columns.size(); z++)
          {
              if( z != 0 )
                  selectSql += ", "; 
              selectSql += columns[z]._name;
          }
          

          【讨论】:

          • 看起来可行。它类似于我提供的第二个示例。
          【解决方案8】:

          我建议构建一个通用连接函数来执行此操作。您可以使用例如累积算法来连接列。

          编辑:litb's implementation;它不那么天真了。

          // 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);
          

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

          【讨论】:

          • 是的,但它可用于 Column 类。更好的方法是以某种方式在模板中附加您想要的字段。
          • 这个想法很好,但实现错误:)。没有非整数非指针/非引用非类型模板参数。所以你不能让它像上面那样接受 std::string :p
          • 哈;我不知道。我不太擅长 STL 算法...抱歉。
          • 不用担心。我已经在我的例子中写了我会怎么做,并给了你信任:)
          猜你喜欢
          • 2014-06-08
          • 2020-12-06
          • 2021-06-25
          • 2020-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多