0

读取最后一行数据后,我的 sqlite3_step 保持 1 秒。为什么?

-(NSDictionary*)specificationItemsForConfigurationsIds:(NSString*)configurationsIdsStr
{
    [self databaseOpen];

    NSString *query = [NSString stringWithFormat:@"SELECT SpecItem.id,SpecItem.name,ConfigurationSpec.configuration_id\
                   FROM (SpecItem INNER JOIN ConfigurationSpec ON ConfigurationSpec.spec_item_id=SpecItem.id)\
                   WHERE (SpecItem.parent_id=12 OR SpecItem.parent_id=34 OR SpecItem.id=23 OR SpecItem.id=27) AND ConfigurationSpec.configuration_id IN (%@)",configurationsIdsStr];

sqlite3_stmt *statement;

NSMutableDictionary* configurationsWithSpecItems = [NSMutableDictionary new];

if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK)
{
    while (sqlite3_step(statement) == SQLITE_ROW)
    {
        int specItemId = sqlite3_column_int(statement, 0);
        NSString* specItemName = [self sqlite3_column_text_asString_ofStatement:statement
                                                                       atColumn:1];
        int configId = sqlite3_column_int(statement, 2);
        NSString* configIdNumber = [NSString stringWithFormat:@"%d",configId];

        NSMutableArray* specItems = [configurationsWithSpecItems objectForKey:configIdNumber];
        if(specItems == nil)
        {
            specItems = [NSMutableArray new];
            [configurationsWithSpecItems setObject:specItems
                                            forKey:configIdNumber];
        }

        SpecificationItem* specItem = [SpecificationItem specificationItemWithId:specItemId
                                                                            name:specItemName];

        [specItems addObject:specItem];
        // When we read last row data, getting from here to POINT 2 takes 1s
    }
    // POINT 2
    sqlite3_finalize(statement);
}
[self databaseClose];
return configurationsWithSpecItems;
}

单行读取需要 2-3 毫秒,但在最后一个退出 while 循环后需要 1 秒,这对我来说太多了。

此查询的 EXPLAIN QUERY PLAN 输出:

0 0 1 SCAN TABLE Configuration (~100000 rows) 
0 0 0 EXECUTE LIST SUBQUERY 1
1 0 0 SEARCH TABLE Configuration USING AUTOMATIC COVERING INDEX (model_id=?) (~7 rows) 
0 1 0 SEARCH TABLE SpecItem USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
4

1 回答 1

0

There are two explanations for the delay; one or both might apply:

  • All the records that match are at the beginning of the Configuration table. After the last matching record, SQLite still has to search through all the remaining records, but none matches.
  • SQLite creates a temporary index on the model_id column because it estimates that the query would be even slower without it. After the query has finished, that index must be deleted again; what you see is the time needed to synchronize at the end of the (automatic) transaction.

Create an index on the model_id column will help avoiding both of these points.

If possible, you should try to merge the subquery (in configurationsIdStr) into the outer query; instead of:

... ConfigurationSpec.configuration_id IN (
           SELECT configuration_id FROM Configuration WHERE model_id = 42)

use something like this:

... ConfigurationSpec.model_id = 42

Avoiding that indirection makes it much easier for SQLite to optimize the query execution.

于 2013-08-09T16:27:19.040 回答