используя вектор имен столбцов, для создания оператора SQL - PullRequest
0 голосов
/ 29 ноября 2008

Проблема, которую нам нужно регулярно решать на моем рабочем месте, заключается в том, как создавать операторы 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";

Я не особо увлечен ни одной из этих реализаций.

Мне интересно услышать идеи о других способах решения этой проблемы, с целью облегчить чтение / понимание / сопровождение кода.

Какие альтернативные методы доступны?

Ответы [ 8 ]

5 голосов
/ 29 ноября 2008

В вашем случае, вероятно, можно предположить, что существует хотя бы один столбец, так как в противном случае нет никакого смысла делать выбор. В этом случае вы можете сделать:

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

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

selectSql += " FROM some-table";
3 голосов
/ 29 ноября 2008

Вместо того, чтобы каждый раз повторять работу, вы можете решить проблему раз и навсегда, написав функциональный объект и используя предложенный тип 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;";
2 голосов
/ 29 ноября 2008

Мы не убираем запятую.
Это потому, что вы можете выбрать константу, а 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";
1 голос
/ 30 ноября 2008

Не для того, чтобы осмыслить точку зрения, но взгляните на boost ::gorith :: 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. У них, как правило, уже есть решение большинства подобных проблем.

1 голос
/ 29 ноября 2008

Это не должно быть так сложно.

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();
}
1 голос
/ 29 ноября 2008

Обычно я строю операторы так:

pad = ""
stmt = "SELECT "

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

Это относительно чисто - оно переназначает заполнение каждой итерации, но это тривиально. Я использовал любое количество тривиальных вариантов, но это самый чистый механизм, который я знаю.

Конечно, у кого-нибудь еще будет ответ, которому можно научиться ...

0 голосов
/ 29 ноября 2008
for (z = 0; z < columns.size(); z++)
{
    if( z != 0 )
        selectSql += ", "; 
    selectSql += columns[z]._name;
}
0 голосов
/ 29 ноября 2008

Я бы предложил создать для этого универсальную функцию соединения. Вы можете использовать, например, алгоритм накопления для объединения столбцов.

РЕДАКТИРОВАТЬ: См. Реализация 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);

Конечно, вы можете получить более чистый синтаксис, используя лучшие обертки.

...