【发布时间】:2011-07-03 23:15:19
【问题描述】:
使用 CoreData 时,以下多列索引谓词非常慢 - 26,000 条记录几乎需要 2 秒。
请注意,这两列都已编入索引,我特意使用 > 和
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"airportNameUppercase >= %@ AND airportNameUppercase < %@ \
OR cityUppercase >= %@ AND cityUppercase < %@ \
upperText, upperTextIncremented,
upperText, upperTextIncremented];
但是,如果我运行两个单独的 fetchRequest,每列一个,然后合并结果,那么每个 fetchRequest 只需 1-2 百分之一秒,合并列表(已排序)大约需要 1/十分之一秒。
这是 CoreData 如何处理多个索引的错误,还是这是预期的行为?以下是我完整的优化代码,运行速度非常快:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init]autorelease];
[fetchRequest setFetchBatchSize:15];
// looking up a list of Airports
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Airport"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// sort by uppercase name
NSSortDescriptor *nameSortDescriptor = [[[NSSortDescriptor alloc]
initWithKey:@"airportNameUppercase"
ascending:YES
selector:@selector(compare:)] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:nameSortDescriptor, nil]autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
// use > and <= to do a prefix search that ignores locale and unicode,
// because it's very fast
NSString *upperText = [text uppercaseString];
unichar c = [upperText characterAtIndex:[text length]-1];
c++;
NSString *modName = [[upperText substringToIndex:[text length]-1]
stringByAppendingString:[NSString stringWithCharacters:&c length:1]];
// for the first fetch, we look up names and codes
// we'll merge these results with the next fetch for city name
// because looking up by name and city at the same time is slow
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"airportNameUppercase >= %@ AND airportNameUppercase < %@ \
OR iata == %@ \
OR icao == %@",
upperText, modName,
upperText,
upperText,
upperText];
[fetchRequest setPredicate:predicate];
NSArray *nameArray = [context executeFetchRequest:fetchRequest error:nil];
// now that we looked up all airports with names beginning with the prefix
// look up airports with cities beginning with the prefix, so we can merge the lists
predicate = [NSPredicate predicateWithFormat:
@"cityUppercase >= %@ AND cityUppercase < %@",
upperText, modName];
[fetchRequest setPredicate:predicate];
NSArray *cityArray = [context executeFetchRequest:fetchRequest error:nil];
// now we merge the arrays
NSMutableArray *combinedArray = [NSMutableArray arrayWithCapacity:[cityArray count]+[nameArray count]];
int cityIndex = 0;
int nameIndex = 0;
while( cityIndex < [cityArray count]
|| nameIndex < [nameArray count]) {
if (cityIndex >= [cityArray count]) {
[combinedArray addObject:[nameArray objectAtIndex:nameIndex]];
nameIndex++;
} else if (nameIndex >= [nameArray count]) {
[combinedArray addObject:[cityArray objectAtIndex:cityIndex]];
cityIndex++;
} else if ([[[cityArray objectAtIndex:cityIndex]airportNameUppercase] isEqualToString:
[[nameArray objectAtIndex:nameIndex]airportNameUppercase]]) {
[combinedArray addObject:[cityArray objectAtIndex:cityIndex]];
cityIndex++;
nameIndex++;
} else if ([[cityArray objectAtIndex:cityIndex]airportNameUppercase] <
[[nameArray objectAtIndex:nameIndex]airportNameUppercase]) {
[combinedArray addObject:[cityArray objectAtIndex:cityIndex]];
cityIndex++;
} else if ([[cityArray objectAtIndex:cityIndex]airportNameUppercase] >
[[nameArray objectAtIndex:nameIndex]airportNameUppercase]) {
[combinedArray addObject:[nameArray objectAtIndex:nameIndex]];
nameIndex++;
}
}
self.airportList = combinedArray;
【问题讨论】:
-
看起来您正试图过早地优化您的请求谓词。您是否尝试过更简单的谓词并发现它们更慢?
-
顺便说一句,您显然在用 SQL 术语考虑这一点,这将导致 Core Data 的悲痛。虽然 Core Data 经常使用 SQLite 存储,但这是最近添加的,实际上与 Core Data 的核心功能无关,即管理内存中的对象图。
标签: objective-c cocoa-touch core-data