Делаем позицию параметра в командах sqlite3_bind динамической

В проекте мне не разрешено использовать сторонний код (например, GRDB, Swift.sqlite), поэтому мне нужно напрямую использовать функции sqlite3 c-wrapper Swift.

Мой оператор SQL является переменным, основанным на определенных флагах.

var sql = "SELECT * FROM myTable WHERE "
sql += " colA = ? "
sql += " AND (colB > ? OR colB < ?)  "

if flagC == true {
  sql += " AND colC = ? "
}

sql += " AND colD = ? "

var stmt = OpaquePointer?

guard sqlite3_prepare_v2(dbPointer, sql, -1, &stmt, nil) == SQLITE_OK
else { print("error") }

guard sqlite3_bind_text(stmt, 1, (myVarA as NSString).utf8String, nil) == SQLITE_OK &&
      sqlite3_bind_double(stmt, 2, myVarB, nil) == SQLITE_OK &&
      sqlite3_bind_double(stmt, 3, myVarB1, nil) == SQLITE_OK 
else { print("error") }

var nextPosition = 4
if flagC == true {
  guard sqlite3_bind_int(stmt, nextPosition, myVarC, nil) == SQLITE_OK
  else { print("error") }
  nextPosition += 1
}

guard sqlite3_bind_double(stmt, nextPosition, myVarD, nil) == SQLITE_OK
else { print("error") }

while sqlite3_step(stmt) == SQLITE_ROW {
  // deal with result
}

Это работает, однако кажется довольно неуклюжим из-за прерывания операторов guard для размещения потенциальных параметров.

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

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

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

Ответы 1

Ответ принят как подходящий

Жестко закодированные числовые индексы так же жестко закодированы, как и сама необработанная строка SQL - не лучше, но и не хуже. Я бы не слишком беспокоился о них. 1, 2, 3? ОК: это первый, второй и третий параметр - это понятно.

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


Одним из возможных улучшений является перемещение теста flagC в конец при построении SQL-запроса:

var sql = "SELECT * FROM myTable WHERE "
sql += " colA = ? "
sql += " AND (colB > ? OR colB < ?)  "
sql += " AND colD = ? "

if flagC == true {
  sql += " AND colC = ? "
}

Вы по-прежнему будете использовать жестко заданные индексы (1, 2, 3, 4), но вам больше не понадобится nextPosition.


Еще один метод заключается в использовании именованных параметров:

var sql = "SELECT * FROM myTable WHERE "
sql += " colA = :a "
sql += " AND (colB > :minB OR colB < :maxB)  "

if flagC == true {
  sql += " AND colC = :c "
}

sql += " AND colD = :d "

Вместо того, чтобы указывать числовые индексы для ваших параметров (1, 2, 3, nextPosition), вы должны использовать sqlite3_bind_parameter_index, чтобы превратить имена параметров (:a, :minB и т. д.) в индексы.


Наконец, еще один подход полностью состоит в том, чтобы заранее протестировать flagC и полностью независимо обрабатывать оба SQL-запроса. Это устранит вашу потребность в nextPosition.

У меня довольно много случаев, подобных примеру flagC, поэтому перемещение в конец не очень помогает. Но использование sqlite3_bind_parameter_index отлично решило бы мою проблему. Спасибо - и я обычно использую GRDB - и мне это нравится. 🤣 Выполнение этого проекта "вручную" действительно дало мне дополнительную оценку! Спасибо за создание этой замечательной библиотеки.

Joseph 17.01.2023 07:47

Спасибо, Джозеф :-) Счастливый SQLite - это многословно, но все же ваш друг!

Gwendal Roué 17.01.2023 09:39

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