fmdb icon indicating copy to clipboard operation
fmdb copied to clipboard

Acceptable pattern for fetching from multiple tables in nested queries?

Open Apocryphon opened this issue 6 years ago • 2 comments

I can have Model Book which contains a column that references Model Author via ID. When fetching from the Book table, I would have to do any additional fetch from Author, if the author column is nonempty. My question is, would the following be a valid way to accomplish this:

Database Singleton class contains a property for a FMDatabaseQueue that is created once.

- (NSArray<Book *>  *)bookObjects
{
    __block NSMutableArray<Book *> *tempBooks = [NSMutableArray new];
    
    NSString *sql = @"SELECT * FROM Book ";
    
    [self.databaseQueue inDatabase:^(FMDatabase * db) {
        FMResultSet *bookFetchResult = [db executeQuery:sql];
        while ([bookFetchResult next]) {
            [tempBooks addObject:[self bookWithFetchResultSet:bookFetchResult
                                                     database:db]];
        }
    }];
    
    return [tempBooks copy];
}

- (Book *)populateBookWithFetchResultSet:(FMResultSet *)bookFetchResult
                                database:(FMDatabase *)db
{
    Book *book = [Book new];
    book.isbn   = [bookFetchResult longForColumn:@"isbn"];
    book.genre  = [bookFetchResult stringForColumn:@"genre"];
    
    if ([bookFetchResult longForColumn:@"author"]) {
        book.author = (Author *)[self authorObjectWithID:@([bookFetchResult longForColumn:@"author"])
                                                                     fromDatabase:db];
    }
    
    return book;
}

- (Author *)authorObjectWithID:(NSNumber)authorID
                  fromDatabase:(FMDatabase *)db
{
    __block Author *author;

    NSString *sql = [NSString stringWithFormat:@"SELECT * FROM Author WHERE authorID= '%lld';", authorID];
    
    [self.databaseQueue inDatabase:^(FMDatabase * db) {
        FMResultSet *authorFetchResult = [db executeQuery:sql];
        if ([authorFetchResult next]) {
                author = [self populateAuthorWithFetchResultSet:authorFetchResult
                                                   fromDatabase:db];
        }
    }];

    return author;
}

So as you can see, while fetching Book, there is a need to do a second fetch to Author, to fully populate the Book model object. All queries are done on the same _databaseQueue, using the same FMDatabase object that is referenced with db, which used in these different methods.

I haven't received any open result set warnings or concurrency issues with this pattern, so far. Is this valid?

Apocryphon avatar Jun 14 '18 18:06 Apocryphon

I may be missing something, but is the "author" column in your Books table the authorID (which it looks like it is from the query to fetch from the Author table)? If so, have you thought about using one query and joining the tables on authorID?

R4N avatar Jun 15 '18 19:06 R4N

The thing is, because Books might reference the ID's of rows from multiple tables, I'm not sure if it's all that efficient to join Book on Author, and on Publisher, etc. All of which might contain references to other tables as well.

Apocryphon avatar Jun 18 '18 20:06 Apocryphon