diff --git a/packages/loot-core/src/server/aql/compiler.js b/packages/loot-core/src/server/aql/compiler.js
index a5ad99f43f8655f2079824c8217665b1c77f0953..48e076ad6e91762255d3de57ef41cd38a5de54f7 100644
--- a/packages/loot-core/src/server/aql/compiler.js
+++ b/packages/loot-core/src/server/aql/compiler.js
@@ -927,7 +927,7 @@ function isAggregateFunction(expr) {
     return true;
   }
 
-  return argExprs.find(ex => isAggregateFunction(ex));
+  return !!argExprs.find(ex => isAggregateFunction(ex));
 }
 
 export function isAggregateQuery(queryState) {
@@ -939,7 +939,7 @@ export function isAggregateQuery(queryState) {
     return true;
   }
 
-  return queryState.selectExpressions.find(expr => {
+  return !!queryState.selectExpressions.find(expr => {
     if (typeof expr !== 'string') {
       let [_, value] = Object.entries(expr)[0];
       return isAggregateFunction(value);
diff --git a/packages/loot-core/src/server/aql/schema/executors.js b/packages/loot-core/src/server/aql/schema/executors.js
index df3c229a98fd4bdec0c5e28ad6ae0f791532a7b1..2be475e7f4d986e99e15a7a815fbf3fa426dc414 100644
--- a/packages/loot-core/src/server/aql/schema/executors.js
+++ b/packages/loot-core/src/server/aql/schema/executors.js
@@ -85,18 +85,28 @@ async function execTransactionsGrouped(
   let { withDead } = queryState;
   let whereDead = withDead ? '' : `AND ${sql.from}.tombstone = 0`;
 
+  // Aggregate queries don't make sense for a grouped transactions
+  // query. We never should include both parent and children
+  // transactions as it would duplicate amounts and the final number
+  // would never make sense. In this case, switch back to the "inline"
+  // type where only non-parent transactions are considered
   if (isAggregateQuery(queryState)) {
-    let allSql = `
-      SELECT ${sql.select}
-      FROM ${sql.from}
-      ${sql.joins}
-      ${sql.where} AND is_parent = 0 ${whereDead}
-      ${sql.groupBy}
-      ${sql.orderBy}
-      ${sql.limit != null ? `LIMIT ${sql.limit}` : ''}
-      ${sql.offset != null ? `OFFSET ${sql.offset}` : ''}
-    `;
-    return db.all(allSql);
+    let s = { ...sql };
+
+    // Modify the where to only include non-parents
+    s.where = `${s.where} AND ${s.from}.is_parent = 0`;
+
+    // We also want to exclude deleted transactions. Normally we
+    // handle this manually down below, but now that we are doing a
+    // normal query we want to rely on the view. Unfortunately, SQL
+    // has already been generated so we can't easily change the view
+    // name here; instead, we change it and map it back to the name
+    // used elsewhere in the query. Ideally we'd improve this
+    if (!withDead) {
+      s.from = 'v_transactions_internal_alive v_transactions_internal';
+    }
+
+    return execQuery(queryState, state, s, params, outputTypes);
   }
 
   let rows;
diff --git a/packages/loot-core/src/server/aql/schema/executors.test.js b/packages/loot-core/src/server/aql/schema/executors.test.js
index 65591868092596e0c067da5dfdfee3a07204bcdc..1df6cf60786c83179ceeabb000e4fbeb03235c05 100644
--- a/packages/loot-core/src/server/aql/schema/executors.test.js
+++ b/packages/loot-core/src/server/aql/schema/executors.test.js
@@ -207,7 +207,7 @@ describe('transaction executors', () => {
 
             let { data } = await runQuery(aggQuery.serialize());
 
-            let sum = arr.reduce((sum, trans) => {
+            let sum = aliveTransactions(arr).reduce((sum, trans) => {
               let amount = trans.amount || 0;
               let matched = (amount < -5 || amount > -2) && trans.payee != null;
               if (!trans.tombstone && !trans.is_parent && matched) {