いくつかのこと:
すべての sqlite3_xxx()
呼び出しの戻りコードを確認する必要があります。あなたの例では、の結果をチェックしませんでしたsqlite3_prepare_v2()
。sqlite3_step()
そのため、その後の呼び出しを実行したかどうかを知る方法はありません。
診断目的で、プログラムが期待どおりに動作しない場合は、INSERT
andUPDATE
ステートメントの後で、特にWHERE
句を含むステートメントの後に値を再取得して、次のことを確認する必要があります。
WHERE
句に一致する行が見つかりました。と
保存された値は、期待どおりでした。
コード サンプルでは、同じ句を使用してデータベースから値を取得したこと、WHERE
実際に行を見つけたこと、値が正しいことを示していませんでした。
これはすべて非常に単純に聞こえると思いますが、これらの手順のいずれも行っていないことは明らかです。そして、あなたのコードを見なければ、あなたの課題の原因を知ることは不可能です. しかし、上記のポイント 1 と 2 に注意を払っていない場合は、わざわざコードを共有する必要はありません。
以下に、あなたが書くべきだと思う種類のコードを紹介します。これは、上記の両方の基準を満たしています。ご覧のとおり、このコードはこれらの各ステップを正しく実行し、すべて正常に動作します。
NSString
また、クラス メソッドstringWithFormat
でSQL ステートメントを作成するのではなく、常に?
SQL ステートメントの変数値にプレースホルダーを使用し、メソッドsqlite3_bind_xxx()
を使用してそれらの値を設定する、以下のコードのより微妙な改良にも注目してください。SQL ステートメントでは常に変数値を「バインド」する必要があります。良い習慣であるだけでなく、TEXT
列の値を引用符で囲むことを心配する必要がなくなり、SQL インジェクション攻撃などから保護されます。
とにかく、ここにいくつかのコードがあります:
// ViewController.m
// sqlite demo
#import "ViewController.h"
#import <sqlite3.h>
@interface ViewController ()
{
sqlite3 *database;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self testSqlite];
}
#pragma mark - SQLite methods
// test my database interaction
- (void)testSqlite
{
if (![self openDb])
return;
if (![self dropTable])
{
[self closeDb];
return;
}
if (![self createTable])
{
[self closeDb];
return;
}
double initialStoredValue = 0.1;
sqlite3_int64 rowId = [self insertRowWithDouble:initialStoredValue];
if (rowId < 0)
{
[self closeDb];
return;
}
double retrievedValue = [self selectDoubleForRow:rowId];
NSLog(@"%s inserted row with value of %f, retrieved value of %f", __FUNCTION__, initialStoredValue, retrievedValue);
double newValue = 0.2;
if (![self updateRow:rowId withDouble:newValue])
{
[self closeDb];
return;
}
retrievedValue = [self selectDoubleForRow:rowId];
NSLog(@"%s updated row with value of %f, retrieved value of %f", __FUNCTION__, newValue, retrievedValue);
if (![self closeDb])
return;
}
// return TRUE if successful, FALSE if not
- (BOOL)openDb
{
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *databasePath = [documentsPath stringByAppendingPathComponent:@"vdos.db"];
if (sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK)
{
NSLog(@"%s database open failed", __FUNCTION__);
return FALSE;
}
return TRUE;
}
// return TRUE if successful, FALSE if not
- (BOOL)dropTable
{
const char *sql = "DROP TABLE IF EXISTS mytable";
char *errmsg;
if (sqlite3_exec(database, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
NSLog(@"%s table drop failed: \"%s\"", __FUNCTION__, errmsg);
return FALSE;
}
return TRUE;
}
// return TRUE if successful, FALSE if not
- (BOOL)createTable
{
const char *sql = "CREATE TABLE IF NOT EXISTS mytable (id INTEGER PRIMARY KEY AUTOINCREMENT, float_column REAL)";
char *errmsg;
if (sqlite3_exec(database, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
NSLog(@"%s table create failed: \"%s\"", __FUNCTION__, errmsg);
return FALSE;
}
return TRUE;
}
// return the primary key of the inserted table
// -1 = failure
- (sqlite3_int64)insertRowWithDouble:(double)value
{
sqlite3_stmt *statement;
const char *sql = "INSERT INTO mytable (float_column) VALUES (?)";
if (sqlite3_prepare(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(@"%s prepare failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return -1;
}
if (sqlite3_bind_double(statement, 1, value) != SQLITE_OK)
{
NSLog(@"%s column bind failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return -1;
}
if (sqlite3_step(statement) != SQLITE_DONE)
{
NSLog(@"%s step failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return -1;
}
if (sqlite3_finalize(statement) != SQLITE_OK)
{
NSLog(@"%s finalize failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return -1;
}
return sqlite3_last_insert_rowid(database);
}
// return float_column value for row with particular rowId
- (double)selectDoubleForRow:(sqlite3_int64)rowId
{
sqlite3_stmt *statement;
const char *sql = "SELECT float_column FROM mytable WHERE id = ?";
if (sqlite3_prepare(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(@"%s prepare failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return FALSE;
}
if (sqlite3_bind_int64(statement, 1, rowId) != SQLITE_OK)
{
NSLog(@"%s column bind failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
sqlite3_finalize(statement);
return FALSE;
}
if (sqlite3_step(statement) != SQLITE_ROW)
{
NSLog(@"%s step failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
sqlite3_finalize(statement);
return FALSE;
}
double value = sqlite3_column_double(statement, 0);
if (sqlite3_finalize(statement) != SQLITE_OK)
{
NSLog(@"%s finalize failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return FALSE;
}
return value;
}
// return the primary key of the inserted table
// -1 = failure
- (BOOL)updateRow:(sqlite3_int64)rowId withDouble:(double)value
{
sqlite3_stmt *statement;
const char *sql = "UPDATE MYTABLE SET float_column = ? WHERE id = ?";
if (sqlite3_prepare(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(@"%s prepare failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return FALSE;
}
if (sqlite3_bind_double(statement, 1, value) != SQLITE_OK)
{
NSLog(@"%s column bind 1 failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
sqlite3_finalize(statement);
return FALSE;
}
if (sqlite3_bind_int64(statement, 2, rowId) != SQLITE_OK)
{
NSLog(@"%s column bind 2 failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
sqlite3_finalize(statement);
return FALSE;
}
if (sqlite3_step(statement) != SQLITE_DONE)
{
NSLog(@"%s step failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
sqlite3_finalize(statement);
return FALSE;
}
if (sqlite3_finalize(statement) != SQLITE_OK)
{
NSLog(@"%s finalize failed: \"%s\"", __FUNCTION__, sqlite3_errmsg(database));
return FALSE;
}
return TRUE;
}
// return TRUE if successful, FALSE if not
- (BOOL)closeDb
{
if (sqlite3_close(database) != SQLITE_OK)
{
NSLog(@"%s database close failed", __FUNCTION__);
return FALSE;
}
database = NULL;
return TRUE;
}
@end
さて、他の質問ですが、iPhone の sqlite データベースから浮動小数点数 (REAL) を読み取るにはどうすればよいですか? INTEGER
、または であるデータベースに格納された値について、REAL
重大な問題は、REAL
値を SQLite に格納するには を使用し、値sqlite3_bind_double
を読み取るには を使用する必要があることです。また、データベースにorステートメントが反映されていない場合は、関数呼び出しの結果を正常にチェックしていないか、句に誤りがあります。REAL
sqlite3_column_double
INSERT
UPDATE
sqlite3_xxx()
WHERE
余談ですが、注意が必要な SQLite の特異性があります。createTable
への呼び出しをcreateTableBad
、 myfloat_column
が実際に として定義されているへの呼び出しに置き換えたと想像してくださいINTEGER
。
// return TRUE if successful, FALSE if not
- (BOOL)createTableBad
{
const char *sql = "CREATE TABLE IF NOT EXISTS mytable (id INTEGER PRIMARY KEY AUTOINCREMENT, float_column INTEGER)";
char *errmsg;
if (sqlite3_exec(database, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
NSLog(@"%s table create failed: \"%s\"", __FUNCTION__, errmsg);
return FALSE;
}
return TRUE;
}
驚くべきことに、これcreateTableBad
を私のtestSqlite
メソッドで使用すると、値の挿入と更新double
が引き続き機能します。これは、SQLite が「動的型システム」であるためです (データ型に関する SQLite の説明の概要を参照してください)。
0.1
の列定義を持つ列にの浮動小数点値を挿入すると、テーブルの列定義に関係なく、INTEGER
実際には値が として格納されます。REAL
その値を に変更すると0.2
、それも機能します。これは、データがテーブルに格納される方法は、ステートメント内の列定義の機能ではなく、挿入方法の機能であるためです。CREATE TABLE
これをさらに説明するために、列の型をHIPPOPOTOMUS
!
// return TRUE if successful, FALSE if not
- (BOOL)createTableReallyBad
{
const char *sql = "CREATE TABLE IF NOT EXISTS mytable (id INTEGER PRIMARY KEY AUTOINCREMENT, float_column HIPPOPOTOMUS)";
char *errmsg;
if (sqlite3_exec(database, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
NSLog(@"%s table create failed: \"%s\"", __FUNCTION__, errmsg);
return FALSE;
}
return TRUE;
}
コードはまだ機能します!
INTEGER
必要に応じて、列の型をまたはとして正確に表すべきではないと言っているのではありませんREAL
。sqlite3_bind_xxx()
SQLite データベース内のデータのデータ型は、ステートメントを挿入したときの関数であり、テーブル定義が列を定義したとされる方法ではないことを理解することが重要であると言っているだけです。