gpt4 book ai didi

sql - Arel 导致聚合无限循环

转载 作者:数据小太阳 更新时间:2023-10-29 07:00:21 26 4
gpt4 key购买 nike

我在使用 Arel 聚契约(Contract)一查询中的 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 个 expense_account_users,每个都有不同的 charges/paid_debts,如下:1 charge/1 payment,2 charges/2 payments,2 charges/1 payment,2 charges/0 payments,1 charge/2 payments, 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

您必须首先在用户和费用之间进行左外连接,以便为每个用户获得一行,然后您必须将结果左外连接到债务,以避免乘以您的两个连接在同一构造内的结果。

(注意使用 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

经过多次尝试,我发现这个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'))

本质上,我在第一个构造中使用 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 OUTER JOIN 合并到一个语句中而不使用内部子选择会导致示例 2、3 和 4 中的 sum_paid 总数成倍增加 - 任何时候都有多个 收费或付款以及至少一个

根据网上的一些阅读,我曾希望稍微改变一下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 - Arel 导致聚合无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3927487/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com