I am looking for ways to write some basic queries in CoreData and there are not examples in the documentation. Following is my query:
So a SQL query to calculate total expenses would be :
select
TOTAL(e.amount * IFNULL(c.rate, 1))
from EXPENSE e
LEFT OUTER join CATEGORY c on
e.category = c.id
How can this be done in CoreData?
A simpler solution might be to implement another method on your Expense class that gives you the appropriately calculated amount.
E.g.
- (NSDecimalNumber *) calculatedExpenseAmount {
NSDecimalNumber *actualAmount = self.expenseAmount;
// Pseudo-code begins
if (self.expenseCategory != nil) {
actualAmount = self.expenseAmount * self.expenseCategory.unitRate;
}
return actualAmount;
}
I'm adding on to my previous answer.
If you want to avoid pulling in every managed object, you could use the NSDictionary-result query to just pull out the expenseAmount and expenseCategory.unitRate values.
- (NSDecimalNumber *) totalExpenses
{
// Fetch all of the expense amounts and unit rate of any related category.
NSFetchRequest *request = ...;
[request setManagedObjectContext:<...>];
[request setEntity:<ExpenseAccountDescription>];
[request setResultType:NSDictionaryResultType];
NSArray *props = [NSArray arrayWithObjects:@"expenseAmount", @"category.unitRate", nil];
[request setPropertiesToFetch:props];
NSArray *amounts = [request executeRequest:...];
// amounts is an array of dictionaries, each hold the desired property values.
// Loop and sum the individual amounts
NSDecimal *total = [[NSDecimalNumber zero] decimalNumber];
NSAutoreleasePool *pool = nil; // contain the mess
NSCalculationError err = NSCalculationNoError;
for (NSDictionary *result in amounts)
{
pool = [NSAutoreleasePool new];
NSDecimal newTotal = [[NSDecimalNumber zero] decimalNumber];
NSDecimalNumber *expenseAmount = [result valueForKeyPath:@"expenseAmount"];
NSDecimalNumber *unitRate = [result valueForKeyPath:@"category.unitRate"];
if (unitRate != nil) {
// do the unit rate multiplication and accumulate the result in the total
NSDecimal calculated = [[NSDecimalNumber zero] decimalNumber];
err = NSDecimalMultiply (&calculated, [expenseAmount decimalNumber], [unitRate decimalNumber], NSRoundBankers);
if (err == NSCalculationNoError) {
err = NSDecimalAdd (&newTotal, total, calculated, NSRoundBankers);
}
}
else {
// just accumulate the result in the total
err = NSDecimalAdd (&newTotal, total, [expenseAmount decimalNumber], NSRoundBankers);
}
// Keep the new total
NSDecimalCopy(&total, newTotal);
[pool drain];
}
return [NSDecimalNumber decimalNumberWithDecimal:total];
}
If you have 10000 expense entries, this fetch and calculation might take less than 1MB of RAM. Instruments would be your friend in measuring that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With