Arel在聚合上造成无限循环

我在使用Arel在同一查询中聚合2列时遇到了问题。 当我运行这个时,整个服务器在rails dev-server崩溃之前冻结一分钟。 我怀疑是一个无限循环:)。

也许我误解了Arel的整个概念,如果有人能看一看,我将不胜感激。

此查询的预期结果如下所示:[{:user_id => 1,:sum_account_charges => 300,:sum_paid_debts => 1000},...]

a_account_charges = Table(:account_charges)
a_paid_debts = Table(:paid_debts)
a_participants = Table(:expense_accounts_users)

account_charge_sum = a_account_charges
  .where(a_account_charges[:expense_account_id].eq(id))
  .group(a_account_charges[:user_id])
  .project(a_account_charges[:user_id], a_account_charges[:cost].sum)

paid_debts_sum = a_paid_debts
 .where(a_paid_debts[:expense_account_id].eq(id))
 .group(a_paid_debts[:from_user_id])
 .project(a_paid_debts[:from_user_id], a_paid_debts[:cost].sum)

charges = a_participants
 .where(a_participants[:expense_account_id].eq(id))
 .join(account_charge_sum)
 .on(a_participants[:user_id].eq(account_charge_sum[:user_id]))
 .join(paid_debts_sum)
 .on(a_participants[:user_id].eq(paid_debts_sum[:from_user_id]))

我对arel很陌生,但是在经历了几天的沉重打击之后,我真的不想做这件事。 以下是我所做的工作概况,如果有人有任何其他见解,欢迎。

首先,这些脚本将创建测试表并用测试数据填充它们。 我已经设置了9个费用帐户,每个帐户都有一组不同的费用/付款_帐户,如下所示:1次/ 1次付款,2次付款/ 2次付款,2次付款/ 1次付款,2次付款/ 0次付款,1次付款/ 2次付款, 0费用/ 2付款,1费用/ 0付款,0费用/ 1付款,0费用,0付款。

CREATE TABLE IF NOT EXISTS `expense_accounts_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `expense_account_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;

INSERT INTO `expense_accounts_users` (`id`, `expense_account_id`) VALUES (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1);

CREATE TABLE IF NOT EXISTS `account_charges` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `expense_account_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `cost` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;

INSERT INTO `account_charges` (`id`, `expense_account_id`, `user_id`, `cost`) VALUES (1, 1, 1, 1), (2, 1, 2, 1), (3, 1, 2, 2), (4, 1, 3, 1), (5, 1, 3, 2), (6, 1, 4, 1), (7, 1, 5, 1), (8, 1, 5, 2), (9, 1, 7, 1);

CREATE TABLE IF NOT EXISTS `paid_debts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `expense_account_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `cost` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=10 ;

INSERT INTO `paid_debts` (`id`, `expense_account_id`, `user_id`, `cost`) VALUES (1, 1, 1, 1), (2, 1, 2, 1), (3, 1, 2, 2), (4, 1, 3, 1), (5, 1, 4, 1), (6, 1, 4, 2), (7, 1, 6, 1), (8, 1, 6, 2), (9, 1, 8, 1);

最终,为了一举获得你所掌握的数据,这是你要使用的SQL语句:

SELECT user_charges.user_id,
  user_charges.sum_cost,
  COALESCE(SUM(paid_debts.cost), 0) AS 'sum_paid'
FROM (
  SELECT expense_accounts_users.id AS 'user_id',
  COALESCE(sum(account_charges.cost), 0) AS 'sum_cost'
  FROM expense_accounts_users
  LEFT OUTER JOIN account_charges on expense_accounts_users.id = account_charges.user_id
  GROUP BY expense_accounts_users.id)
AS user_charges
LEFT OUTER JOIN paid_debts ON user_charges.user_id = paid_debts.user_id
GROUP BY user_charges.user_id

您必须首先在用户和费用之间执行LEFT OUTER JOIN,以便为每个用户获得一行,然后您必须将LEFT OUTER JOIN 结果加入债务,以避免将结果与同一构造内的两个连接相乘。

(注意使用COALESCE将NULL值从LEFT OUTER JOIN转换为零 - 也许是一种便利项目)

这个陈述的结果是这样的:

user_id   sum_cost  sum_paid
1         1         1
2         3         3
3         3         1
4         1         3
5         3         0
6         0         3
7         1         0
8         0         1
9         0         0

经过多次尝试后,我发现这些代码与我们之后最接近:

c = Arel::Table.new(:account_charges)
d = Arel::Table.new(:paid_debts)
p = Arel::Table.new(:expense_accounts_users)
user_charges = p
 .where(p[:expense_account_id].eq(1))
 .join(c, Arel::Nodes::OuterJoin)
 .on(p[:id].eq(c[:user_id]))
 .project(p[:id], c[:cost].sum.as('sum_cost'))
 .group(p[:id])
charges = user_charges
 .join(d, Arel::Nodes::OuterJoin)
 .on(p[:id].eq(d[:user_id]))
 .project(d[:cost].sum.as('sum_paid'))

从本质上讲,我加入用户在第一个构建中用LEFT OUTER JOIN收费,然后尝试将该结果和LEFT OUTER JOIN返回到债务。 这个arel代码产生下面的SQL语句:

SELECT `expense_accounts_users`.`id`,
  SUM(`account_charges`.`cost`) AS sum_cost,
  SUM(`paid_debts`.`cost`) AS sum_paid
FROM `expense_accounts_users`
LEFT OUTER JOIN `account_charges` ON `expense_accounts_users`.`id` = `account_charges`.`user_id`
LEFT OUTER JOIN `paid_debts` ON `expense_accounts_users`.`id` = `paid_debts`.`user_id`
WHERE `expense_accounts_users`.`expense_account_id` = 1
GROUP BY `expense_accounts_users`.`id`

运行时产生这个输出:

id  sum_cost  sum_paid
1   1         1
2   6         6
3   3         2
4   2         3
5   3         NULL
6   NULL      3
7   1         NULL
8   NULL      1
9   NULL      NULL

非常接近,但不完全。 首先,缺少COALESCE会给我们NULL值而不是零 - 我不确定如何在AREL中影响COALESCE函数调用。

更重要的是,虽然,结合LEFT外部联接到一个语句而无需内部子选择被导致sum_paid总计在实施例2,3和4中得到倍频-任何时候有任何电荷或付款和至少一个多于一个另一个。

根据一些在线阅读材料,我曾希望稍微改变arel可以解决这个问题:

charges = user_charges
 .join(d, Arel::Nodes::OuterJoin)
 .on(user_charges[:id].eq(d[:user_id]))
 .project(d[:cost].sum.as('sum_paid'))

但是,无论何时,我在第二个arel构造中使用user_charges []时,我都会在SelectManager#[]中遇到未定义的方法错误。 这可能是一个错误,或者可能是对的 - 我真的不知道。

我只是没有看到arel有办法使用第一个构造中的SQL作为第二个构造中的可查询对象,并具有必要的子查询别名,这是在一个SQL语句中实现这一点所需的。

链接地址: http://www.djcxy.com/p/48385.html

上一篇: Arel causing infinite loop on aggregation

下一篇: Sproutcore vs Cappuccino speed