Итак, у меня есть несколько мест в моей программе, где я просто извлекаю одну строку (иногда даже один столбец в одной строке), например
SELECT id,title,posted FROM posts WHERE id=4;
Поскольку это вернет только одну строку (если только я не сосу материал с базой данных и каким-то образом умудряюсь иметь дублированные идентификаторы), я чувствую, что слишком сложно пройти весь путь sqlite3_prepare_v2(...)
, sqlite3_bind_int(...)
, sqlite3_step(...)
, sqlite3_finalize(...)
только для получения одной строки.
Я знаю, что это можно сделать с помощью sqlite3_exec(...)
и с помощью функции обратного вызова для обработки результатов, которые также return 1;
прерывают дальнейшие выполнения, но в моем случае это не работает, потому что мой запрос выглядит так:
SELECT id,title,posted FROM posts WHERE id=?;
И у вас не может быть параметризованных запросов с sqlite3_exec(...)
. Вставка идентификатора через какой-то printf(...)
здесь тоже небезопасна.
Итак, это то, что я делаю прямо сейчас
char *sql;
sqlite3_stmt *stmt;
sql = sqlite3_mprintf(
"SELECT \
title, \
((julianday(posted) - 2440587.5) * 86400), \
text \
FROM \
%s \
WHERE id=?;",
POSTS_TABLE); /* printf safe here, POSTS_TABLE is a #define */
if (sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL) != SQLITE_OK)
show_sqlite_error(db);
else
{
sqlite3_bind_text(stmt, 1, id, strlen(id), SQLITE_TRANSIENT);
sqlite_code = sqlite3_step(stmt); /* This right here seems just ugly */
if (sqlite_code != SQLITE_ROW) /* especially this */
printf("<div id=\"error\">ERROR: No such ID in database</div>\n");
else
{
int i;
char time_string[25];
const unsigned char *title = sqlite3_column_text(stmt, 0);
time_t time = sqlite3_column_int(stmt, 1);
const unsigned char *text = sqlite3_column_text(stmt, 2);
strftime(time_string, 25, DATE_FORMAT, gmtime(&time));
printf("<h2 id=\"post_headline\">%s</h2>\n\n", title);
printf("<h3 id=\"post_time\">%s</h3>\n", time_string);
printf("<p id=\"post_body\">\n");
for (i = 0; i < strlen((char *)text); i++)
if (text[i] == '\n')
printf("<br />");
else
printf("%c", text[i]);
}
}
Моя проблема с этим кодом заключается в том, что я вызываю sqlite3_step()
один раз, выдаю ошибку, если было возвращено все, кроме SQLITE_ROW
, отображаю все, что нужно отобразить, если было возвращено SQLITE_ROW
, и вызываю finalize, фактически не переходя к SQLITE_DONE
. Хотя это (как мне сказали) не причинит никакого вреда оператору SELECT
, оно кажется уродливым и излишним.
Другое дело — запросы, которые просто возвращают одно значение.
SELECT MAX(id) FROM posts;
В моем случае это просто возвращает «5». Ни больше ни меньше. Особенно здесь это действительно излишество, чтобы пройти весь путь подготовки, привязки, шага, финализации, но я ДОЛЖЕН сделать это таким образом, потому что мой запрос снова выглядит как
SELECT MIN(id) FROM posts WHERE id > ?;
например, чтобы получить следующий идентификатор после текущего. Он просто возвращает «5», когда текущий идентификатор равен «4».
Подводя итог: есть ли быстрый способ получить результаты одной строки из оператора SELECT
без явного зацикливания до тех пор, пока SQLITE_DONE
не будет получено из sqlite3_step()
И как мне получить одну строку, одно значение столбца (в основном скалярное), не повторяя весь путь снова, включая звонок sqlite_column_...
?