2010-10-13 45 views
7

Tôi gặp sự cố khi sử dụng Arel để tổng hợp 2 cột trong cùng một truy vấn. Khi tôi chạy điều này, toàn bộ máy chủ bị đóng băng trong một phút, trước khi các máy chủ dev bị treo. Tôi nghi ngờ một vòng lặp vô hạn :).Arel gây ra vòng lặp vô hạn trên tập hợp

Có lẽ tôi đã hiểu lầm toàn bộ khái niệm về Arel, và tôi sẽ biết ơn nếu có ai có thể nhìn vào nó.

Kết quả dự kiến ​​của truy vấn này là một cái gì đó như thế này: [{: 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])) 
+1

Bạn đã giải quyết vấn đề này chưa? –

+0

loại SQL nào được sản xuất cho bạn? – nessur

+0

Bạn đã đăng một lỗi? – DNNX

Trả lời

4

tôi m mới để arel, nhưng sau khi đập vào điều này trong vài ngày và thực sự đào, tôi không nghĩ rằng nó có thể được thực hiện. Dưới đây là một phác thảo về những gì tôi đã làm, nếu bất cứ ai có bất kỳ cái nhìn sâu sắc bổ sung nó sẽ được chào đón.

Đầu tiên, các tập lệnh này sẽ tạo các bảng thử nghiệm và điền chúng với dữ liệu thử nghiệm. Tôi đã thiết lập 9 cost_account_users, mỗi gói có một khoản phí/khoản thanh toán khác nhau, như sau: 1 khoản phí/1 thanh toán, 2 khoản phí/2 khoản thanh toán, 2 khoản phí/1 thanh toán, 2 khoản phí/0 thanh toán, 1 khoản phí/2 khoản thanh toán, 0 khoản phí/2 khoản thanh toán, 1 khoản phí/0 khoản thanh toán, 0 khoản phí/1 lần thanh toán, 0 khoản phí, 0 khoản thanh toán.

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); 

Cuối cùng, để có được những dữ liệu mà bạn đang theo đuổi trong một ngã swoop, đây là câu lệnh SQL bạn muốn sử dụng:

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 

Bạn phải làm một LEFT OUTER JOIN giữa người sử dụng và phí đầu tiên để bạn có được một hàng cho mỗi người dùng, sau đó bạn phải LEFT OUTER JOIN kết quả cho các khoản nợ để tránh nhân kết quả của bạn với hai tham gia bên trong cùng một cấu trúc.

(chú ý sử dụng liên hiệp để chuyển đổi các giá trị NULL từ LEFT OUTER JOIN để zero - một mục tiện nghi, có lẽ)

Kết quả của tuyên bố này là thế này:

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 

Sau nhiều lần thử, tôi thấy rằng mã arel này đến gần nhất với những gì chúng tôi đang theo sau:

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')) 

Về cơ bản, tôi là jo đưa người dùng vào các chi phí trong cấu trúc đầu tiên bằng LEFT OUTER JOIN, sau đó cố gắng lấy kết quả của nó và LEFT OUTER JOIN nó trở lại các khoản nợ. đang AREL này tạo ra các câu lệnh SQL sau:

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` 

nào, khi chạy, sản lượng sản xuất này:

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 

Rất gần, nhưng không hoàn toàn. Đầu tiên, việc thiếu COALESCE cho chúng ta giá trị NULL thay vì zeroes - Tôi không chắc chắn làm thế nào để thực hiện cuộc gọi hàm COALESCE từ bên trong arel.

Quan trọng hơn, kết hợp LEFT OUTER JOIN thành một câu lệnh không có subselect bên trong là kết quả tổng cộng sum_paid nhân lên trong ví dụ 2, 3 và 4 - bất kỳ lúc nào có nhiều hơn một hoặc thanh toán và ít nhất một của người kia.

Dựa trên một số bài đọc trực tuyến, tôi đã hy vọng rằng việc thay đổi AREL hơi sẽ giải quyết vấn đề:

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

Nhưng bất cứ lúc nào tôi đã sử dụng user_charges [] trong xây dựng AREL thứ hai, tôi đã nhận ra lỗi phương pháp xác định cho SelectManager # []. Đây có thể là một lỗi, hoặc có thể đúng - tôi thực sự không thể nói.

Tôi không thấy rằng arel có cách để tận dụng SQL từ cấu trúc đầu tiên như một đối tượng có thể truy vấn trong cấu trúc thứ hai với bí danh truy vấn con cần thiết, như là cần thiết để thực hiện điều này trong một câu lệnh SQL .

+2

Bạn có thể thêm hàm COALESCE bằng cách tạo hàm được đặt tên: Arel :: Nodes :: NamedFunction.new (: COALESCE, [t [: your_column], 0) sẽ tạo ra kết quả SQL đúng. –

+0

Thật tuyệt vời! Cảm ơn vì điều đó! – Yardboy

+0

nơi t = arel_table bên trong định nghĩa lớp học của bạn. –

Các vấn đề liên quan