views:

387

answers:

3

I have 2 entities, A and B that have a many to many relationship.

The A entity has about 10,000 objects in, and B has about 20 objects.

Basically, A objects can be related to one or more B objects, and the B objects keep track of which A objects they are connected to. This is done with the inverse relationship setting.

I simply wish to return every B object that is not related to an A object. The fetch I am using is this:

NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setIncludesPropertyValues:NO];
[fetch setEntity:[NSEntityDescription entityForName:@"B" inManagedObjectContext:context]];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"aObjects.@count == 0"]];
return [context executeFetchRequest:fetch error:nil];

However, it the fetch execution is taking a very long time, approx. 30 seconds. I don't understand this, because although there is a large number of A objects, this fetch has nothing to do with them, and just needs to check the 20 B objects.

If I comment out the predicate so the fetch returns all of the B objects, then the fetch is really quick, as you'd expect for just fetching 20 objects. So it would seem that that predicate is getting some A objects involved and is causing it to take a long time!

Can anyone shed any light on why this is taking so long?

Edit:

I've got SQL debug information and here's what's output:

CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK FROM ZTABLEVIEWOBJECT t0 WHERE ((SELECT COUNT(*) FROM ZTABLEVIEWOBJECT t1 JOIN Z_10BOBJECTS t2 ON t1.Z_PK = t2.Z_10AOBJECTS3 JOIN ZTABLEVIEWOBJECT t3 ON t2.Z_12BOBJECTS = t3.Z_PK WHERE (t0.Z_PK = t2.Z_12BOBJECTS) ) = ? AND  t0.Z_ENT = ?) 
CoreData: annotation: sql connection fetch time: 49.4198s
CoreData: annotation: total fetch execution time: 49.4240s for 0 rows.

I should add that both entity A and entity B inherit (have a parent) of a common TableViewObject entity, which holds common values between the two (such as table view section names and sort names etc). Hope this helps!

+2  A: 

If you're using an SQLite store, you can turn on SQL debug logging. Core Data will log all the SQL commands it's making to the console. I've found this very useful to discover something unexpected happening behind the scenes. It will also print out timing information for each SQL command!

To do this, double-click the Executable in your project, go to the Arguments tab, and add an argument:

-com.apple.CoreData.SQLDebug 1
Ken Aspeslagh
Thanks for the suggestion, however I believe the SQLDebug option isn't available on the iPhone.
Michael Waterfall
Yes, it does work on the iPhone. I did it yesterday. If you Apple-R from Xcode, and run on the Device, stdout will go to XCode's console.
Ken Aspeslagh
Ah great, it's working! Right, I'll see what SQL is being run.
Michael Waterfall
Okay, I've added the output to the question, any idea?
Michael Waterfall
Wow, 50 seconds! It looks like Core Data is doing a JOIN, which means it has to check all the entries. You might want to just add an optimization where a BObject sets an attribute hasAnAObject ;)
Ken Aspeslagh
Ahh, I've just read up that it may be because of the inheritance (as they share a SQL table) causing all the rows to be processed. Would this make sense? Is the JOIN an additional problem or part of the same issue with the joint table? See: https://devforums.apple.com/message/153715#153715
Michael Waterfall
It certainly looks like that's what happening. I think Marcus is better suited to know. Dude wrote the book on it. ;)
Ken Aspeslagh
Oh yea, wow! Thanks for your help :-)
Michael Waterfall
+1  A: 

If I am understanding you correctly, you have a many-to-many relationship between these objects and that may be causing your issue. In a many-to-many there is a join table between B and A and that join table is probably getting hit hard. I would be quite curious to see the SQL that is being generated with your fetch.

UPDATE

Try changing your predicate to be "aObjects == nil" and see what happens. Because it is a many-to-many relationship that is the only other predicate that I can think of that might give you the results you are looking for.

Marcus S. Zarra
I've just added the SQL output to the question, hope this helps diagnose it.
Michael Waterfall
Ahh, I've just read up that it may be because of the inheritance (as they share a SQL table) causing all the rows to be processed. Would this make sense? Is the JOIN an additional problem or part of the same issue with the joint table? See: devforums.apple.com/message/153715#153715
Michael Waterfall
Yeah the reason for this is the many-to-many relationship, and not the inheritance! I removed all the inheritance (took forever) only to find out it didn't make much of a difference! Thanks for your help.
Michael Waterfall
Thanks for that update - interesting thought! I'll give it a try and get back to you!
Michael Waterfall
+1  A: 

Hi Micheal,

you can replace

aObjects.@count == 0

with

ANY aObjects == nil

It's drastically reduce the time of my query (iPhone OS 3.0). It also works for != nil ( @count != 0 ).

Danilo Bonardi