Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/54.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql Arel导致聚合上的无限循环_Sql_Ruby On Rails_Ruby_Ruby On Rails 3_Arel - Fatal编程技术网

Sql Arel导致聚合上的无限循环

Sql Arel导致聚合上的无限循环,sql,ruby-on-rails,ruby,ruby-on-rails-3,arel,Sql,Ruby On Rails,Ruby,Ruby On Rails 3,Arel,我在使用Arel聚合同一查询中的2列时遇到问题。当我运行这个程序时,整个服务器会冻结一分钟,然后rails开发服务器就会崩溃。我怀疑有一个无限循环:) 也许我误解了阿雷尔的整个概念,如果有人能看一看,我将不胜感激 此查询的预期结果如下所示: [{:user\u id=>1,:sum\u账户费用=>300,:sum\u已支付债务=>1000},] a_account_charges = Table(:account_charges) a_paid_debts = Table(:paid_debts

我在使用Arel聚合同一查询中的2列时遇到问题。当我运行这个程序时,整个服务器会冻结一分钟,然后rails开发服务器就会崩溃。我怀疑有一个无限循环:)

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

此查询的预期结果如下所示: [{:user\u id=>1,:sum\u账户费用=>300,:sum\u已支付债务=>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]))

我是阿雷尔的新手,但在连续几天的努力和挖掘之后,我认为这是不可能做到的。下面是我所做工作的概要,如果有人有任何其他的见解,那将是受欢迎的

首先,这些脚本将创建测试表并用测试数据填充它们。我已经建立了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
首先必须在用户和费用之间进行左外连接,以便为每个用户获得一行,然后必须将结果左外连接到债务,以避免将结果与同一构造内的两个连接相乘

(注意使用合并将左外部联接中的空值转换为零-可能是一个方便的项目)

该声明的结果如下:

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
经过多次尝试,我发现arel代码最接近我们的目标:

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'))
本质上,我在第一个构造中使用左外连接将用户连接到费用中,然后尝试将结果和左外连接返回到债务中。此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为我们提供了空值而不是零-我不确定如何从arel内部影响COALESCE函数调用

然而,更重要的是,在没有内部子选择的情况下,将左外联接合并到一个语句中,会导致示例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语句中实现这一点所需要的那样。

这为您生成了什么样的SQL?我从未解决过这个问题,也从未继续开发这个玩具项目,因此我从未进一步讨论过这个问题。您可以通过创建一个命名函数来添加COALESCE函数:Arel::Nodes::NamedFunction。新建(:COALESCE,[t[:your_column],0)将生成正确的SQL输出。其中t=Arel_表位于类定义中。