由于缺乏GroupBy和Sum in Realm [iOS],操作非常慢

时间:2017-08-29 12:51:23

标签: swift realm

我正在尝试建立一个会计应用程序,其中可以有许多帐户,每个帐户可以有许多条目。每个条目都有日期和金额。

条目链接到Account类,如下所示:

let entries = LinkingObjects(fromType: Entry.self, property: "account").sorted(byKeyPath: #keyPath(Entry.date))

我想按帐户名进行分组,在任何给定日期之间每月的金额总和。日期可能因报告目的而有所不同。

Realm没有groupby函数我不能轻易得到一个结果,其中列是帐户名,总计,平均值,sumOf(month1),sumOf(month2)等等

因此;我需要在代码中执行此操作,但结果非常慢。我的代码中是否有任何特定的缺失会显着提高计算的速度?

此代码正在为每个帐户和每个期间运行(对于每个帐户的年度报告,这意味着每个帐户12次)以及导致缓慢的原因:

    let total: Double = realm
        .objects(Entry.self)
        .filter("_account.fullPath == %@", account.fullPath)
        .filter("date >= %@ AND date <= %@", period.startDate, period.endDate)
        .filter("isConsolidation != TRUE")
        .sum(ofProperty: "scaledAmount")

以下是我必须循环每个帐户并为每个时段运行查询的完整代码:

private func getMonthlyValues(for accounts: [Account], in realm: Realm, maxLevel: Int) -> String {
    guard accounts.count > 0 else { return "" }
    guard accounts[0].displayLevel < maxLevel else { return "" }

    var rows: String = ""
    for account in accounts.sorted(by: { $0.fullPath < $1.fullPath }) {
        if account.isFolder {
            let row = [account.localizedName.htmlColumn].htmlRow
            rows += row
            rows += monthlyValues(for: Array(account.children), in: realm, maxLevel: maxLevel)
        } else {
            var row: [String] = []
            var totals: [Double] = []

            // period has a start date and an end date
            // monthlyPeriods returns the period in months
            // for a datePeriod starting February 10, and ending in November 20 (for whatever reason)
            // monthlyPeriods returns 10 periods for each month starting from [February 10 - February 28],
            // and ending [November 1 - November 20]
            // below gets the some for the given period for the current account
            // for this example it runs 10 times for each account getting the sum for each period
            for period in datePeriod.monthlyPeriods {
                let total: Double = realm
                    .objects(Entry.self)
                    .filter("_account.fullPath == %@", account.fullPath)
                    .filter("date >= %@ AND date <= %@", period.startDate, period.endDate)
                    .filter("isConsolidation != TRUE")
                    .sum(ofProperty: "scaledAmount")

                totals.append(total)
            }

            let total = totals.reduce(0, +)
            guard total > 0 else { continue }
            let average = AccountingDouble(total / Double(totals.count)).value
            row.append("\(account.localizedName.htmlColumn)")
            row.append("\(total.htmlFormatted().htmlColumn)")
            row.append("\(average.htmlFormatted().htmlColumn)")
            row.append(contentsOf: totals.map { $0.htmlFormatted().htmlColumn })
            rows += row.htmlRow
        }
    }
    return rows
}

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

由于您没有使用Results的自动更新属性,我会尝试不是每个月运行不同的查询,而是使用单个查询获取所有条目,然后使用Swift构建过滤和求和在filterreduce函数中,这样可以减少Realm查询的开销。

只需let entries = realm.objects(Entry.self)使用单个查询存储所有条目,然后您可以使用Array(entries).filter({$0.account.fullPath == account.fullPath})等对每个帐户和日期进行过滤,并且每次您都不会查询Realm做计算。如果您的条目在计算之间不会发生变化,您甚至可以使用let entries = Array(realm.objects(Entry.self))立即将条目投射到数组