2011-01-20 33 views
5

Estoy buscando formas de escribir algunas consultas básicas en CoreData y no hay ejemplos en la documentación. Lo que sigue es mi consulta:Ayuda con CoreData Query

  1. tengo un objeto de gastos y tiene un campo expenseAmount.
  2. Los gastos se pueden vincular a un objeto ExpenseCategory.
  3. ExpenseCategory puede simplemente definir la categoría del gasto (por ejemplo, Alimentos) o puede tener una tasa unitaria (por ejemplo, Kilometraje). Si solo se trata de un nombre, el valor de gasto es el valor de costo en el objeto Expense, de lo contrario es expenseAmount * unitRate en ExpenseCategory.
  4. Como el enlace de gastos a categoría es opcional, el gasto final se basaría en la presencia/ausencia de categoría y la tasa de unidad.

Así que una consulta SQL para calcular los gastos totales serían:

select 
    TOTAL(e.amount * IFNULL(c.rate, 1)) 
from EXPENSE e 
LEFT OUTER join CATEGORY c on 
    e.category = c.id 

¿Cómo se puede hacer esto en CoreData?

+0

Gracias por esta pregunta. Tengo un problema similar y NSExpression no funcionó para mí: estoy trabajando con dobles y marcas de tiempo y, por alguna razón, la función de dividir por: produce resultados extraños. Tengo curiosidad acerca de su solución final, ya que también necesito incluir más de un registro en mi cálculo. –

Respuesta

2

Una solución más simple podría ser implementar otro método en su clase de Gastos que le proporcione la cantidad calculada adecuadamente.

E.g.

- (NSDecimalNumber *) calculatedExpenseAmount { 

    NSDecimalNumber *actualAmount = self.expenseAmount; 

    // Pseudo-code begins 
    if (self.expenseCategory != nil) { 
    actualAmount = self.expenseAmount * self.expenseCategory.unitRate; 
    } 

    return actualAmount; 
} 

estoy añadiendo a mi respuesta anterior.

Si desea evitar tirar de cada objeto administrado, puede usar la consulta NSDictionary-result para simplemente extraer los valores de expenseAmount y expenseCategory.unitRate.

- (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]; 
} 

Si tiene 10000 entradas de gastos, esta búsqueda y cálculo pueden tomar menos de 1MB de RAM. Los instrumentos serían tus amigos para medir eso.

+0

Esto funcionaría por un solo gasto. Estoy tratando de cotejar todos los gastos en DB sin leerlos todos en la memoria. – siasl

+0

Gracias por la ayuda. Intenté usar multiplicar: por: de la clase NSExpression para hacer este cálculo en sqlite. Simulator lanzó una excepción RT y creo que todavía no está disponible en iPhone. Lo que no entiendo aquí es por qué CoreData no tendría soporte para algo tan simple. Sí, sé que CD piensa en términos de gráfico de objetos, al final todo se trata de datos. – siasl

+0

Estoy bastante seguro de que no hay forma de que el CD ejecute SQL arbitrario. Si eres realmente persistente, podrías intentar ir directamente a SQLite. Es compatible con todas las cosas que desea hacer. –