Почему моя область для модели с двумя внешними ключами работает некорректно?

#ruby-on-rails #rails-activerecord

#ruby-on-rails #rails-activerecord

Вопрос:

У меня есть модели Category и Transaction .

Category has_many transactions , Transaction belongs_to category .

Transaction имеет столбцы debit_amount и credit_amount .

Я использую область, которая отображает категории и сортирует их по сумме transaction.debit_amount и transaction_credit_amount :

 Category.joins(:transactions)
      .where('transactions.created_at >= ?', 1.month.ago)
      .select(
        'categories.*',
        'SUM(transactions.credit_amount_cents   transactions.debit_amount_cents) AS total_amount'
      )
      .order('total_amount DESC')
      .group('categories.id')
      .limit(10)
 

И это работает.

Кроме того, у меня есть модель Account . То же самое, Account has many transactions , Transaction belongs_to Account.

Но Account имеет два внешних ключа в Transaction :

transaction.rb:

   belongs_to :credit_account, class_name: 'Account',
    foreign_key: 'credit_account_id', optional: true
  belongs_to :debit_account, class_name: 'Account',
    foreign_key: 'debit_account_id', optional: true
 

account.rb:

   has_many :debit_account_transactions,
    class_name: 'Transaction',
    foreign_key: 'debit_account_id',
    dependent: :nullify
  has_many :credit_account_transactions,
    class_name: 'Transaction',
    foreign_key: 'credit_account_id',
    dependent: :nullify
 

Я хочу использовать область также для учетных записей. Я немного переделал ее:

 Account.joins(:credit_account_transactions, :debit_account_transactions)
      .where('transactions.created_at >= ?', 1.month.ago)
      .select(
        'accounts.*',
        'SUM(transactions.credit_amount_cents   debit_account_transactions_accounts.debit_amount_cents) AS total_amount'      )
      .order('total_amount DESC')
      .group('accounts.id')
 

Но область видимости работает некорректно: (

По какой-то причине учетные записи с transaction.debit_amount == nil не включены в образец.

UPD. SQL:

 SELECT accounts.*, 
SUM(transactions.credit_amount_cents   debit_account_transactions_accounts.debit_amount_cents) AS total_amount 
FROM "accounts" INNER JOIN "transactions" ON 
"transactions"."credit_account_id" = "accounts"."id" INNER JOIN 
"transactions" "debit_account_transactions_accounts" ON 
"debit_account_transactions_accounts"."debit_account_id" = "accounts"."id" GROUP BY accounts.id
 

Комментарии:

1. Поскольку это внутреннее объединение, оно будет включать только те результаты, которые совпадают в объединенной таблице. Используется .left_join для создания внешнего соединения, которое будет включать строки, даже если в объединенной таблице нет строки.

2. @max если я использую left_joins left_outer_joins учетные записи or, которые transaction.debit_amount == nil будут включены в область, но они total_amount будут 0

3. Вероятно, вам следует использовать COALESCE(debit_account_transactions_accounts.debit_amount_cents, 0) для обработки потенциальных нулевых значений. В большинстве баз данных NULL 1 равно NULL, а не 1, поэтому все еще несколько удивительно, что вы получаете 0, а не NULL. db-fiddle.com/f/n29Q8xMyDAdag5cGydH5dD/1