GROUP BY equivalent for Core Data
I know that I can use @distinctUnionOfObjects to find something like the following in SQL:
SELECT a_value
FROM my_table
GROUP BY a_value;
What I'm looking for is all of the data returned in an array, not just the array of values that matches the group by expression. Essentially, I'm looking for the equivalent of the following SQL query for core data:
SE开发者_JS百科LECT *
FROM my_table
GROUP BY a_value;
It's analog
SELECT 'Status', COUNT(*) FROM 'Records' GROUP BY 'Status'
:
NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:@"Record"];
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Record"
inManagedObjectContext:myManagedObjectContext];
NSAttributeDescription* statusDesc = [entity.attributesByName objectForKey:@"status"];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"url"]; // Does not really matter
NSExpression *countExpression = [NSExpression expressionForFunction: @"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetch setPropertiesToFetch:[NSArray arrayWithObjects:statusDesc, expressionDescription, nil]];
[fetch setPropertiesToGroupBy:[NSArray arrayWithObject:statusDesc]];
[fetch setResultType:NSDictionaryResultType];
NSError* error = nil;
NSArray *results = [myManagedObjectContext executeFetchRequest:fetch
error:&error];
Found here
You could try using an NSFetchedResultsController object to provide grouping by way of the sectionNameKeyPath construct in the initializer. Note that FRCs are mainly to be used to couple with a table view, but it's not really necessary. This way you could group your results by your sectionNameKeyPath which could be a transient attribute in your model, too.
As a comment, I wouldn't recommend thinking of Core Data in terms of a database, which it isn't. Core Data is built to make it easier for you to persist and manage object relationships. Just because on the iOS it runs on top of SQLite doesn't make it a database replacement.
Reference: NSFRC Reference
I find this method (roughly similar to the accepted answer) to be a little cleaner and easier to understand. This is the SQL equivalent to:
SELECT COUNT(*), a_value FROM my_table GROUP BY a_value;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[MyTable className]];
// create expression for grouped column
NSExpressionDescription *aValueDescription = [[NSExpressionDescription alloc] init];
aValueDescription.name = @"aValue"; // name of key in result dictionary
aValueDescription.expression = [NSExpression expressionWithFormat:@"aValue"];
aValueDescription.expressionResultType = NSObjectIDAttributeType;
// create expression for count
NSExpressionDescription *countDescription = [[NSExpressionDescription alloc] init];
countDescription.name = @"count"; // name of dictionary key in result dictionary
countDescription.expression = [NSExpression expressionWithFormat:@"aValue.@count"];
countDescription.expressionResultType = NSInteger32AttributeType;
// fill out fetch request
fetchRequest.propertiesToGroupBy = @[@"aValue"];
fetchRequest.propertiesToFetch = @[aValueDescription, countDescription];
//fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"count" ascending:NO]]; // not sure why this crashes
fetchRequest.resultType = NSDictionaryResultType; // required for "group by" requests
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
The returned results
is an array of NSDictionary. Note that the description name
properties can be anything you want - they are just the names of the keys in the returned dictionaries. One can add a predicate to the fetch request to filter rows from the table; this code returns all rows.
Bonus points to anyone who can tell me how to make the sort descriptor work...
I finally found a solution by using NSFetchedResultsController:
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:GROUPBYKEY cacheName:nil]
It will return the sections having grouped objects. Then you can do your operations on it.
You can use Predicate Programming.
EDIT: Sorry you can not use predicates for Group By at least not straight-forward. I just read on the references.
Limitations: You cannot necessarily translate “arbitrary” SQL queries into predicates or fetch requests. There is no way, for example, to convert a SQL statement such as
SELECT t1.name, V1, V2
FROM table1 t1 JOIN (SELECT t2.name AS V1, count(*) AS V2
FROM table2 t2 GROUP BY t2.name as V) on t1.name = V.V1
精彩评论