-2

単一クエリと動的クエリを実装したい。group by句を使用してテーブルをフィルタリングする4つのテーブルがあります。しかし今、これらの 4 つのテーブルを組み合わせて動的クエリを作成したいと考えています。値を選択すると、以下のようになります。

NSString *strQuery = @"Select * from TableName where "

ステップ1 :

entity1 クエリは次のようになります。

Select * from TableName where entity1 = 'S'

ステップ2

エンティティ 2 クエリは次のようになります。

Select * from TableName where entity1 = 'S' AND entity 2 = '100S' 

ステップ:3

エンティティ 3 クエリは次のようになります。

Select * from TableName where entity1 = 'S' AND entity 2 = '100S' AND entity3 = 'Fly'
4

3 に答える 3

1

いくつかの考え:

  1. 列名にスペースが含まれていないことが最善です。スペースがある場合は、列名を引用符で囲む必要があります。

    Select * from TableName where entity1 = 'S' AND 'entity 2' = '100S'
    

    entity2.ではなく、列名にスペースが含まれていないことを確認して、この問題を完全に回避することをお勧めしますentity 2

  2. を使用して SQL を作成するよりも、SQL でプレースホルダーを使用してから、 を使用して値を設定するstringWithFormat方が安全です。このようにして、手動で作成された SQL ステートメントを台無しにする可能性のある文字が変数に含まれている場合 (たとえば、フィールドに単一引用符文字がある場合)、その問題を回避します。だから、あなたは持っているかもしれません:?sqlite3_bind_text()'

    sqlite3 *database;
    if (sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK)
        NSLog(@"%s open error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));
    
    NSString *sql = @"Select * from TableName where entity1 = ? AND entity2 = ?";
    NSString *entity1 = @"S";
    NSString *entity2 = @"100S";
    
    sqlite3_stmt *statement;
    
    if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK)
        NSLog(@"%s prepare SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));
    
    if (sqlite3_bind_text(statement, 1, [entity1 UTF8String], -1, NULL) != SQLITE_OK)
        NSLog(@"%s bind entity 1 error '%s'", __FUNCTION__, sqlite3_errmsg(database));
    
    if (sqlite3_bind_text(statement, 2, [entity2 UTF8String], -1, NULL) != SQLITE_OK)
        NSLog(@"%s bind entity 2 error '%s'", __FUNCTION__, sqlite3_errmsg(database));
    
    int rc;
    
    while ((rc = sqlite3_step(statement)) == SQLITE_ROW)
    {
        // do whatever you want with the results
    
        const unsigned char *entity1 = sqlite3_column_text(statement, 0);
        const unsigned char *entity2 = sqlite3_column_text(statement, 1);
        const unsigned char *entity3 = sqlite3_column_text(statement, 2);
    
        NSLog(@"%s %s %s", entity1, entity2, entity3);
    }
    
    if (rc != SQLITE_DONE)
        NSLog(@"%s step SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));
    
    sqlite3_finalize(statement);
    sqlite3_close(database);
    

    確かに、この の使用はsqlite_bind_text少し面倒ですが、より堅牢です。探しているフィールドに一重引用符が含まれている可能性があるかどうかを完全に確信できない場合は、この規則を使用することが重要です。ユーザーから提供された検索基準に基づいて SQL を作成する場合は重要です (たとえば、「Joe's Bar and Grill」を検索します。「Joe's」のアポストロフィは、sqlite3_bind_textコードを使用しないか、別の方法でコーディングしないと、少し心痛を引き起こす可能性があります)。この問題について)。これは Web サイトよりもアプリにとっては問題ではありませんが、アプリがSQL インジェクションの影響を受けないようにしたいだけです。

  3. 上記のsqlite_bind_text構文は、SQLite 用のすばらしい小さな Objective-C ラッパーであるFMDBを使用すると、大幅に簡素化されます。

    FMDatabase *database = [FMDatabase databaseWithPath:databasePath];
    NSAssert(database, @"unable to open database");
    
    if (![database open])
        NSLog(@"%@", [self.database lastErrorMessage]);
    
    FMResultSet *rs = [database executeQuery:@"Select * from TableName where entity1 = ? AND entity2 = ?", @"S", @"100S"];
    NSAssert(rs, [self.database lastErrorMessage]);
    
    while ([rs next])
    {
        NSLog(@"%@ %@ %@", rs[0], rs[1], rs[2]);
    }
    
    [rs close];
    [database close];
    

    sqlite_bind_textこれにより、雑草のようsqliteな関数呼び出しに煩わされることなく、 の利点を享受できます。


可変数のパラメーターで呼び出すことができる関数が必要な場合は、SQL を動的に構築して、さまざまな方法でそれを行うことができます。コードが可読性の問題に悩まされるため、手動で SQL を構築するのは本当に好きではないという免責事項を以下の例に示します。倹約的なコードをより読みやすいもので犠牲にしたいのですが、あなたの質問から、SQLを動的に構築する方法を知りたいと推測しているので、上記の警告とともにサンプルを提供します. また、これに取り組む方法はたくさんありますが、これはその一例にすぎません。

とにかく、どの列がどの値を持っているかのパラメーターとして辞書を使用したいと仮定しましょう。次のように呼び出すことができます。エンティティ 1 しかない場合は、次のように呼び出します。

[self selectTableWithDictionary:@{@"entity1":@"S"}];

または、エンティティ 1 とエンティティ 2 の両方がある場合は、次のように呼び出します。

[self selectTableWithDictionary:@{@"entity1":@"S", @"entity2":@"100S"}];

次に、このディクショナリを解析し、手動で SQL を作成してから、個々の列をバインドする方法は次のようになります。

- (void)selectWithDictionary:(NSDictionary *)dictionary
{
    NSArray *fieldNames = [dictionary allKeys];
    NSArray *values = [dictionary allValues];

    // build the sql

    NSMutableString *sql = [NSMutableString stringWithString:@"Select * from TableName"];
    if ([fieldNames count]){
        [sql appendString:@" where "];
        [sql appendString:[fieldNames componentsJoinedByString:@" = ? AND "]];
        [sql appendString:@" = ?"];
    }

    sqlite3_stmt *statement;

    // prepare the sql

    if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK)
        NSLog(@"%s prepare SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));

    // bind the values

    for (NSInteger i = 0; i < [fieldNames count]; i++)
        if (sqlite3_bind_text(statement, i+1, [values[i] UTF8String], -1, NULL) != SQLITE_OK)
            NSLog(@"%s bind column # %d error '%s'", __FUNCTION__, i+1, sqlite3_errmsg(database));

    int rc;

    // iterate through the results

    while ((rc = sqlite3_step(statement)) == SQLITE_ROW)
    {
        const unsigned char *entity1 = sqlite3_column_text(statement, 0);
        const unsigned char *entity2 = sqlite3_column_text(statement, 1);
        const unsigned char *entity3 = sqlite3_column_text(statement, 2);

        NSLog(@"%s %s %s", entity1, entity2, entity3);
    }

    if (rc != SQLITE_DONE)
        NSLog(@"%s step SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));

    sqlite3_finalize(statement);
}

FMDB の実装は次のようになります。

- (void)selectTableWithDictionary:(NSDictionary *)dictionary
{
    NSArray *fieldNames = [dictionary allKeys];
    NSArray *values = [dictionary allValues];

    // build the sql

    NSMutableString *sql = [NSMutableString stringWithString:@"Select * from TableName"];
    if ([fieldNames count]){
        [sql appendString:@" where "];
        [sql appendString:[fieldNames componentsJoinedByString:@" = ? AND "]];
        [sql appendString:@" = ?"];
    }

    // execute the sql

    FMResultSet *rs = [self.database executeQuery:sql
                             withArgumentsInArray:values];
    NSAssert(rs, [self.database lastErrorMessage]);

    // iterate through the results

    while ([rs next])
    {
        NSLog(@"%@ %@ %@", rs[0], rs[1], rs[2]);
    }

    [rs close];
}
于 2013-01-02T06:50:01.697 に答える
0

NSStringの「stringWithFormat」メソッドを次のように試すことができます。

NSString *strQuery = [NSString stringWithFormat:@"Select * from TableName where entity1 = '%@' AND entity 2 = '%@'",@"S",@"100S"];

お役に立てば幸いです。

乾杯!

于 2013-01-02T05:53:06.853 に答える
0

静的値の場合は試すことができます

NSString *strQuery =@"Select * from TableName where entity1 = 'S' AND entity 2 = '100S'";

動的な値については、試すことができます

NSString *strQuery = [NSString stringWithFormat:@"Select * from TableName where entity1 = '%@' AND entity 2 = '%@'",entity1_obj,entity2_obj];
于 2013-01-02T06:32:16.987 に答える