I’m trying to translate the following SQL query to Ecto Query DSL:
SELECT fj.*, ft1.*, ft2.*
FROM fin_journal fj
LEFT JOIN (
SELECT ft.*, fa.customer_id
FROM fin_transactions ft
LEFT JOIN fin_accounts fa ON fa.id = ft.fin_account_id
WHERE fa.type = 'customer'
) ft1 ON ft1.fin_journal_entry_id = fj.id
LEFT JOIN (
SELECT ft.*, fa.partner_id FROM fin_transactions ft
LEFT JOIN fin_accounts fa ON fa.id = ft.fin_account_id
WHERE fa.type = 'partner'
) ft2 ON ft2.fin_journal_entry_id = fj.id
WHERE ft1.customer_id = 1 OR ft2.partner_id = 2
(this is simplified version, the acual query is more complex)
As you can see, it selects all transactions of ‘customer’ accounts, all transactions of ‘partner’ accounts, joins them to common journal entry and filters entries belonging to specific customer or to specific partner. In the result each journal entry has either one (customer’s or partner’s) or both transactions.
So let’s try to write it in Ecto DSL:
ft1_query =
from(FinTransaction, as: :fin_transaction)
|> join(:left, [fin_transaction: ft], fa in assoc(ft, :fin_account), as: :fin_account)
|> where([fin_account: fa], fa.type == "customer")
ft2_query =
from(FinTransaction, as: :fin_transaction)
|> join(:left, [fin_transaction: ft], fa in assoc(ft, :fin_account), as: :fin_account)
|> where([fin_account: fa], fa.type == "partner")
from(FinJournal, as: fin_journal_entry)
|> join(:left, [fin_journal_entry: fje], ft1 in subquery(ft1_query), on: ft1.fin_journal_entry_id == fje.id, as: fin_transaction_1)
|> join(:left, [fin_journal_entry: fje], ft2 in subquery(ft2_query), on: ft2.fin_journal_entry_id == fje.id, as: fin_transaction_2)
|> where([fin_transaction_1: ft1, fin_transaction_2: ft2], ...)
|> preload([fin_transaction_1: ft1, fin_transaction_2: ft2], [fin_transaction_1: ft1, fin_transaction_2: ft2])
Here in the where
clause we can’t reference :fin_account
from subqueries to filter on their fields. Also we can’t use preloads in subqueries to add the financial accounts data to their results. If we use select
in subqueries to select customer_id
and partner_id
, then we can’t select whole transaction struct and should select each field individually and then somehow reconstruct the struct in the parent query - this approach seems error-prone.
So is there any way this query could be implemented without questionable techniques like listing all schema fields in the query?