sql – 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]))

解决方法

我是新手,但是经过几天的敲打和真正的挖掘之后,我认为不能做到.这里是我所做过的概述,如果有人有任何额外的见解,那将是受欢迎的.

首先,这些脚本将创建测试表并使用测试数据填充它们.我已经设置了9个expense_account_users,每个都有不同的费用/ paid_debts,如下所示: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,(3,(4,(5,(6,(7,(8,(9,1);

CREATE TABLE IF NOT EXISTS `account_charges` (
  `id` int(11) NOT NULL AUTO_INCREMENT,`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,2,2),3,4,5,7,1);

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

INSERT INTO `paid_debts` (`id`,6,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加入到债务中,以避免将结果与同一构造中的两个连接相乘.

(注意使用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加入到债务中.这个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 OUTER JOIN组合成一个没有内部子选择的语句会导致sum_paid总数在示例2,3和4中成倍增加 – 任何时候不止一次收费或付款且至少有一次另一个.

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

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语句中实现这一点所需要的.

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 'EastRiver' 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...