Используя вектор имен столбцов, чтобы сгенерировать оператор sql

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

Меня не особо привлекает ни одна из этих реализаций.

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
1 129
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Обычно я строю утверждения:

pad = ""
stmt = "SELECT "

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

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

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

Я использую аналогичный метод в своем быстром и грязном коде, за исключением того, что использую bool, чтобы проверить, нужно ли мне печатать запятую.

strager 29.11.2008 04:45

Я собирался сказать, что вы заканчиваете запятыми. Потом я увидел, что ты там делал. Я бы сказал, что этот код немного «сложен», что затрудняет его сопровождение.

John Dibling 29.11.2008 05:21

Кроме того, вы получаете избыточные назначения для «пэда», что, если это высокопроизводительный цикл, будет расточительным.

John Dibling 29.11.2008 05:22

@John Dibling: зависит от языка. Чаще всего для меня в C pad - это указатель const char, и присваивание тривиально. Для выполнения инструкции SQL потребуется гораздо больше времени, чем для ее создания.

Jonathan Leffler 29.11.2008 07:55

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

Обновлено: См. реализация 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);

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

Это так, но его можно использовать повторно для классов Column. Лучшим подходом было бы добавить какое-то поле в шаблон.

strager 29.11.2008 07:54

идея хорошая, но реализация неправильная :). нет никаких параметров шаблона, не являющихся целочисленными, не указательными / не ссылочными. поэтому вы не можете просто заставить его принимать std :: string, как указано выше: p

Johannes Schaub - litb 29.11.2008 21:57

Ха! Я этого не знал. Я не очень хорошо разбираюсь в алгоритмах STL ... Извините.

strager 29.11.2008 22:12

не беспокойся. Я написал в своем примере, как бы это сделать, и дал вам за это должное :)

Johannes Schaub - litb 29.11.2008 22:18
for (z = 0; z < columns.size(); z++)
{
    if ( z != 0 )
        selectSql += ", "; 
    selectSql += columns[z]._name;
}

Похоже, это работает. Он похож на второй приведенный мною пример.

EvilTeach 29.11.2008 06:35
Ответ принят как подходящий

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

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

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

selectSql += " FROM some-table";

Выглядит тоже чисто, без тестов.

EvilTeach 29.11.2008 06:11

В цикле for неявно есть тест :)

Ates Goral 01.12.2008 22:30

Я, конечно, имею в виду дополнительные тесты в теле цикла или дополнительную обработку вне цикла, чтобы удалить последнюю запятую :)

EvilTeach 03.12.2008 05:45

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

Хорошая идея. "Почему я не подумал об этом?"

strager 29.11.2008 06:17

Это красиво упрощает. Можно злоупотребить техникой создания столбца и значений в операторе вставки.

EvilTeach 29.11.2008 06:56

Теперь вы возвращаете дополнительные данные для каждой строки возвращаемых данных с сервера, что означает дополнительный трафик по сети. Используйте дополнительную миллиардную долю секунды, чтобы отсортировать ваши столбцы / запятые, вместо того, чтобы принимать удары, которые умножаются на возвращенные строки (которые могут быть большими).

Tom H 29.11.2008 19:01

(продолжение) Забыл включить ... это также может означать дополнительную память, используемую на сервере и на клиенте.

Tom H 29.11.2008 19:02

@Tom H: Да, торговля есть, как и во всех решениях CS. Вы должны измерить увеличение времени / пространства и посмотреть, стоит ли оно уменьшения сложности (а значит, облегчения сопровождения) кода.

Martin York 29.11.2008 20:13

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

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

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

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

Другие вопросы по теме