Felipe Cypriano

You're about to read

Core Data - How to Do a SELECT DISTINCT

Actually it’s pretty simple to do. But if you try just by reading the documentation you’ll find the same problem I did.

Suppose that I need to get a list of all ages I had on my database to make a hierarchical navigation that starts by selecting an age. If were working directly with the SQLite store I could execute this SQL to do this:

1
2
3
SELECT DISTINCT AGE
FROM PEOPLE
ORDER BY AGE

Since the project uses Core Data I shouldn’t access the underlying database store directly. Use NSFetchRequest class instead of plain SQL. The returnsDistinctsResults property seems to do the trick, its documentation warns about a special consideration:

Returns a Boolean value that indicates whether the fetch request returns only distinct values for the fields specified by propertiesToFetch.

Special Considerations

This value is only used if a value has been set for propertiesToFetch.

So it’s saying is that the returnsDistinctsResults only works if the property propertiesToFetch is also set. Pretty straightforward. Except that there’s one more - undocumented - property that needs to be set: resultType.

The default value of resultType is NSManagedObjectResultType which returns the expected managed objects. To make the distinct results works the value needs to be set to NSDictionaryResultType.

Select Distinct Using Core Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"People" inManagedObjectContext:managedObjectContext];
request.entity = entity;
request.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:@"age"]];
request.returnsDistinctResults = YES;
request.resultType = NSDictionaryResultType;

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptors]];
[sortDescriptor release];

NSError *error = nil;
NSArray *distincResults = [managedObjectContext executeFetchRequest:request error:&error];
// Use the results
[request release];

If you forget to set the resultType there will be no errors, but the returnsDistinctResults will be ignored and your result will have all ages of the database.