В проекте мне не разрешено использовать сторонний код (например, 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
.
Кажется неправильным «жестко закодировать» положение привязок. Есть ли способ решить эту проблему без жесткого кодирования этих позиций?
Жестко закодированные числовые индексы так же жестко закодированы, как и сама необработанная строка 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
.
Спасибо, Джозеф :-) Счастливый SQLite - это многословно, но все же ваш друг!
У меня довольно много случаев, подобных примеру
flagC
, поэтому перемещение в конец не очень помогает. Но использованиеsqlite3_bind_parameter_index
отлично решило бы мою проблему. Спасибо - и я обычно использую GRDB - и мне это нравится. 🤣 Выполнение этого проекта "вручную" действительно дало мне дополнительную оценку! Спасибо за создание этой замечательной библиотеки.