2014-08-28 3 views
2

У меня есть модель Invoice, которая has_many линий и has_many платежей.Rails/SQL: поиск счетов, проверка двух сумм

Invoice: 
    id 
    ref 

Line: 
    invoice_id: 
    total (decimal) 

Payment: 
    invoice_id: 
    total(decimal) 

Мне нужно найти все оплаченные счета-фактуры. Поэтому я делаю следующее:

Invoice.joins(:lines, :payments).having(' sum(lines.total) = sum(payments.total').group('invoices.id') 

Какие запросы:

SELECT * 
FROM "invoices" 
INNER JOIN "lines" ON "lines"."invoice_id" = "invoices"."id" 
INNER JOIN "payments" ON "payments"."invoice_id" = "invoices"."id" 
GROUP BY invoices.id 
HAVING sum(lines.total) = sum(payments.total) 

Но всегда возвращает пустой массив, даже если есть счета оплачены полностью.

Что-то не так с моим кодом?

+0

Так «оплаченные счета» определены .. как? 'sum (total)' всех связанных строк в строке 'равно 'sum (total)' всех связанных строк в 'payment'? –

ответ

0

Если вы присоединяетесь к нескольким таблицам с соотношением 1: n, объединенные строки могут умножать друг друга.
Этот родственный ответ имеет более подробное объяснение проблемы:

Чтобы избежать этого, просуммировать итоговые суммы перед тем вы присоединитесь. Таким образом вы присоединяетесь к точно 1 (или 0) строкам, и ничего не умножается. Не только правильно, но и значительно быстрее.

SELECT i.*, l.sum_total 
FROM invoices i 
JOIN (
    SELECT invoice_id, sum(total) AS sum_total 
    FROM lines 
    GROUP BY 1 
    ) l ON l.invoice_id = i.id 
JOIN (
    SELECT invoice_id, sum(total) AS sum_total 
    FROM payments 
    GROUP BY 1 
    ) p ON p.invoice_id = i.id 
WHERE l.sum_total = p.sum_total; 

Использование [INNER] JOIN, не LEFT [OUTER] JOIN по назначению. Счета, которые не имеют никаких линий или платежей, не представляют интереса для начала. Поскольку мы хотим «оплатить» счета-фактуры. Из-за отсутствия определения и внешнего вида предоставленного запроса, я предполагаю, что это счета-фактуры с фактическими линиями и платежами, причем оба они одинаковы.

0

Если один счет-фактура есть линия и два платежа полностью оплачиваемую, как это:

lines: 
id  total invoice_id 
1   30   1 

payments: 
id  total invoice_id 
1   10   1 
2   20   1 

Затем присоединиться к линии и платежи счета-фактуры с invoce_id получите 2 строки, как это:

payment_id payment_total line_id line_total invoice_id 
1      10   1   30   1 
2      20   1   30   1 

Так сумма line_total не будет равна сумме payment_total.

Для того, чтобы получить все оплаченный счет-фактура может использовать существует вместо объединений:

Invoice.where(
     "exists 
     (select 1 from 
      (select invoice_id 
      from (select invoice_id,sum(total) as line_total 
       from lines 
       group by invoice_id) as l 
      inner join (select invoice_id,sum(total) as payment_total 
       from payments 
       group by invoice_id) as p 
      on l.invoice_id = p.invoice_id 
      where payment_total = line_total) as paid 
     where invoices.id = paid.id) ") 

sub_query заплатил получит все платные invoice_ids.

Смежные вопросы