私はアレルを初めて使用しますが、これを数日間叩いて本当に掘り下げた後、それができるとは思いません。これが私が行ったことの概要です。誰かが追加の洞察を持っているなら、それは大歓迎です。
まず、これらのスクリプトはテスト テーブルを作成し、テスト データを入力します。次のように、それぞれ異なる料金/支払済債務のセットを持つ9つのexpense_account_usersを設定しました: 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 して、結果が同じ構造内の 2 つの結合と乗算されないようにする必要があります。
(LEFT OUTER JOIN の NULL 値をゼロに変換するCOALESCEの使用に注意してください - おそらく便利なアイテムです)
このステートメントの結果は次のとおりです。
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 を負債に戻そうとしています。このアレル コードは、次の 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 を 1つのステートメントに結合すると、例 2、3、および4でsum_paid の合計が乗算される結果になることです。もう一方の。
いくつかのオンラインの読み物に基づいて、アレルを少し変更することで問題が解決することを期待していました。
charges = user_charges
.join(d, Arel::Nodes::OuterJoin)
.on(user_charges[:id].eq(d[:user_id]))
.project(d[:cost].sum.as('sum_paid'))
しかし、2 番目の arel コンストラクトで user_charges[] を使用するたびに、SelectManager#[]に対して未定義のメソッド エラーが発生しました。これはバグかもしれませんし、正しいかもしれません - 私にはわかりません。
1 つの SQL ステートメントでこれを実現するために必要なように、arel には、必要なサブクエリ エイリアスを持つ 2 番目のコンストラクトのクエリ可能なオブジェクトとして、最初のコンストラクトの SQL を利用する方法があるとは思えません。