2

float valueその値をSQLiteデータベースに更新することを読み取ろうとしています。REALfloat 値の型として使用します。以下は、更新に使用するコードですSQLite

//update download percent
sqlite3_stmt    *statement;
const char *dbpath = [databasePath UTF8String];

if (sqlite3_open(dbpath, &myDB) == SQLITE_OK)
{
    const char *update_stmt = [[NSString stringWithFormat:@"UPDATE myTable SET floatValueColumn=%f WHERE id=%i", myProgress, myTargetId] UTF8String];

    if( sqlite3_prepare_v2(myDB, update_stmt, -1, &statement, NULL) == SQLITE_OK )
    {
        if(SQLITE_DONE != sqlite3_step(statement)){ //sql fail

            NSLog(@"error: %s", sqlite3_errmsg(myDB));
        }
        sqlite3_reset(statement);
        // Finalize and close database.
        sqlite3_finalize(statement);
    }
    // close database
    sqlite3_close(myDB);
}

上記のコードを使用してもエラーは発生せず、このリンクのガイドに従って浮動小数点値を読みました。iphone の sqlite データベースから浮動小数点数 (REAL) を読み取るにはどうすればよいですか? 、しかし、なぜ期待値が得られないのかわかりません。

UPDATE: 次を使用してデータベース ファイルを作成します。

NSString *docsDir;
NSArray *dirPaths;

// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

docsDir = [dirPaths objectAtIndex:0];

// Build the path to the database file
databasePath = [[NSString alloc]
                initWithString: [docsDir stringByAppendingPathComponent:
                                 @"mydbs.db"]];

NSFileManager *filemgr = [NSFileManager defaultManager];

if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
    const char *dbpath = [databasePath UTF8String];

    if (sqlite3_open(dbpath, &myDB) == SQLITE_OK)
    {
        char *errMsg;
        const char *sql_stmt =
        "CREATE TABLE IF NOT EXISTS MYTABLE (MYFLOATVALUECOLUMN REAL)";

        if (sqlite3_exec(myDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
        {
            //alert
        }
        sqlite3_close(myDB);
    } else {
        //alert
    }
}
4

1 に答える 1

2

いくつかのこと:

  1. すべての sqlite3_xxx()呼び出しの戻りコードを確認する必要があります。あなたの例では、の結果をチェックしませんでしたsqlite3_prepare_v2()sqlite3_step()そのため、その後の呼び出しを実行したかどうかを知る方法はありません。

  2. 診断目的で、プログラムが期待どおりに動作しない場合は、INSERTandUPDATEステートメントの後で、特に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ステートメントが反映されていない場合は、関数呼び出しの結果を正常にチェックしていないか、句に誤りがあります。REALsqlite3_column_doubleINSERTUPDATEsqlite3_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必要に応じて、列の型をまたはとして正確に表すべきではないと言っているのではありませんREALsqlite3_bind_xxx()SQLite データベース内のデータのデータ型は、ステートメントを挿入したときの関数であり、テーブル定義が列を定義したとされる方法ではないことを理解することが重要であると言っているだけです。

于 2012-12-06T07:53:29.913 に答える