由于类型为date的核心数据模型属性包含日期和时间,因此将其编入索引是否有意义?在其他数据库环境中,我认为不使用太少或太多的唯一值来索引属性是一种好习惯。
在我的特定情况下,绝大多数日期都是零。我的谓词看起来像这样:
NSPredicate *subPredicate = [NSPredicate predicateWithFormat:@"xDate == nil || xDate < %@", [NSDate date]];
目前我的xDate字段没有编入索引,典型的提取大约需要2.4秒,这对IMO只有1300条返回记录来说太长了。
答案 0 :(得分:1)
经过数小时的调试和分析后,我得出的结论是,如果核心数据确定有更好的方法,就不能让核心数据使用索引。
这个blog post帮助我理解了如何使用SQLite的EXPLAIN QUERY PLAN
来分析用于执行提取的策略。
基本上,我所做的是:
在Xcode中:
-com.apple.CoreData.SQLDebug 1
在终点站:
/usr/bin/sqlite3
.open
命令,其中包含我之前登录到调试器控制台的数据库的路径。我是这样做的,因为我有多个持久的商店。EXPLAIN QUERY PLAN
。您还可以向sqlite3发出.indices
命令,以验证索引是否符合预期。
显然,这仅适用于模拟器,因为您无法在终端上打开设备上的数据库。
为了让在多个数据存储区和模拟器位置之间移动更加容易,我在设置持久存储之后调用此方法:
- (void)examineQuery
{
NSMutableString *result = [NSMutableString string];
[result appendString: @"\n/usr/bin/sqlite3"];
[result appendFormat: @"\n.open '%@'", [self.coreDataStack.databaseURL path]];
[result appendString: @"\nEXPLAIN QUERY PLAN "];
NSLog(@"%@",result);
}
我只是从调试器中复制所有3行并将它们粘贴到终端中。然后我在调试器中找到记录的SELECT
语句,将其复制并粘贴到终端中。您需要在完成的;
命令末尾键入EXPLAIN QUERY PLAN
,以便sqlite3终止输入并处理命令。
在我的情况下,sqlite3 EXPLAIN QUERY PLAN
给出了这个结果:
SCAN TABLE ZART AS t0 USING INDEX ZART_ZNAME_INDEX
好的,它正在使用不同的索引。看看我的NSFetchRequest我现在可以假设排序描述符正在确定索引。
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kEntityName];
request.sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)],
nil];
我更改了代码以保证xDate永远不会为NULL,以确定OR子句是否导致核心数据不在xDate上使用我的索引。我现在能够在模型中使xDate不是可选的并且改变了我的谓词:
[NSPredicate predicateWithFormat:@"xDate > %@", [NSDate date]]
sqlite3 EXPLAIN QUERY PLAN
给出了相同的结果:
SCAN TABLE ZART AS t0 USING INDEX ZART_ZNAME_INDEX
我尝试的最后一件事是在xDate,name上创建一个复合索引。再次通过EXPLAIN QUERY PLAN
过程产生了相同的结果。
在我调查期间,我遇到了多个核心数据优化提取的引用,但没有保证索引使用。我假设那是我击中的墙。
奇怪的是,即使没有使用xDate和xDate,也没有使用名称索引,我的提取现在已降至.21秒。我相信这一改进来自于从模拟器中删除我的应用程序并进行全新安装。我的所有索引使用测试都是在安装新应用程序之后完成的(在您提出之前;)
似乎向我的模型添加标记并未传播到已安装的数据库。我确实尝试首先迁移到新版本的数据库,但它似乎仍然没有添加新的标记。
答案 1 :(得分:0)
是的,它肯定会隐藏查询结果。我试过了。
我有大量数据,结果是 - 没有索引~2秒 索引〜。 - 。 - 2秒