核心数据 - NSPredicate 用于具有相同列且满足另一个条件的对象

Posted

技术标签:

【中文标题】核心数据 - NSPredicate 用于具有相同列且满足另一个条件的对象【英文标题】:Core Data - NSPredicate for objects with identical column and also meet another condition 【发布时间】:2015-11-11 21:09:27 【问题描述】:

我正在寻找一个谓词来获取Entity 类型的所有托管对象,其值在属性sessionId 中重复,其中所有组(“组”,表示sessionId 相等的托管对象) 属性processed 中的内容标志设置为YES。这可以(慢慢地)完成,但我正在为此寻找一种高效的班轮。谢谢

这是缓慢的方式:

NSFetchRequest *request = [NSEntityDescription entityForName:@"Entity"
                                      inManagedObjectContext:context];
NSArray *all = [context executeFetchRequest:request error:nil];
NSArray *sessionIds = [all valueForKeyPath:@"@distinctUnionOfObjects.sessionId"];
NSMutableArray *objects = [NSMutableArray array];
for (NSString *sessionId in sessionIds) 
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"sessionId == %@", sessionId];
    NSArray *inSession = [all filteredArrayUsingPredicate:predicate];
    for(id obj in inSession) 
         if(![obj valueForKey:@"processed"]) continue;
    

    [objects arrayByAddingObjectsFromArray:processed];

NSLog(@"%@", objects);

【问题讨论】:

给我们看一些代码。你试过什么? 这是一个开放式问题——不是“请修复我的代码” @Skyler - 也许你应该听听 Cliff 的建议。至少显示“缓慢”的方式,因为您的描述令人困惑且不清楚。 “其值在列中重复”是什么意思? “处理列中所有组的内容标志的位置”应该是什么意思?此外,也许您在评论中使用了错误的术语,但开放式问题不适合堆栈溢出:***.com/help/dont-ask @Jody 添加示例代码 你的方法有多“慢”?你是怎么测量的? 【参考方案1】:

由于布尔值存储为 0 和 1,因此所有行都具有 processed = YES 的组将具有 average(processed) = 1。因此,您可以使用NSFetchRequestpropertiesToGroupByhavingPredicate 来获得满足您条件的sessionIds。然后需要第二次获取以获取具有任何 sessionIds 的 Entity 对象:

NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = @[@"sessionId"];
fetch.propertiesToGroupBy = @[@"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: @"average:(processed) == 1"];
NSArray *resultsArray = [context executeFetchRequest:fetch error:nil];
NSArray *sessionIdArray = [resultsArray valueForKeyPath:@"sessionId"];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:@"name IN %@",sessionIdArray];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(@"Final results, %@", finalResults);

抱歉,它不是单行的。我把它留给你来确定它是否比你自己的代码更快。

编辑

要一次性完成所有操作,请使用 NSFetchRequestExpression 代替中间数组:

NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = @[@"sessionId"];
fetch.propertiesToGroupBy = @[@"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: @"average:(processed) == 1"];
NSExpression *fetchExpression = [NSFetchRequestExpression expressionForFetch:[NSExpression expressionForConstantValue:fetch] context:[NSExpression expressionForConstantValue:context] countOnly:false];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:@"sessionId IN %@",fetchExpression];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(@"Final results, %@", finalResults);

请注意,在我的(诚然微不足道的)测试设置中,这实际上比两次获取解决方案运行得更慢。

仅供参考,如果您使用 SQLDebug 构建设置来检查生成的 SQL,它看起来像这样:

SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZSESSIONID, t0.ZPROCESSED FROM ZENTITY t0 WHERE t0.ZSESSIONID IN (SELECT n1_t0.ZSESSIONID from ZENTITY n1_t0 GROUP BY n1_t0.ZSESSIONID HAVG(n1_t0.ZPROCESSED) = ?)

【讨论】:

感谢您的回复。这可以一次而不是两次完成吗?我想将尽可能多的计算分配到 SQLite 级别而不是 CocoaTouch 层 有趣的是你应该问 - 几天前我在研究另一个问题时才发现。我会更新我的答案。

以上是关于核心数据 - NSPredicate 用于具有相同列且满足另一个条件的对象的主要内容,如果未能解决你的问题,请参考以下文章

NSPredicate 用于查询中的位置

NSPredicate 获取具有 NSDate 日期范围内的日期属性的核心数据对象

具有多对多关系的核心数据 NSPredicate

保存核心数据相关数据并使用具有多个实体的 NSPredicate 和 NSFetchedResultsController 检索

带有核心数据对象的 NSPredicate

具有多对多关系的核心数据 - 在 SUBQUERY 中使用 ALL 创建 NSPredicate