Execute Subquery refactoring first before any other SQL Execute Subquery refactoring first before any other SQL oracle oracle

Execute Subquery refactoring first before any other SQL


Are these transformations really that complex you have to use UNION ALL? It's really hard to optimize something you can't see, but have you maybe tried getting rid of the CTE and implementing your calculations inline?

CREATE OR REPLACE VIEW loan_vw ASSELECT loan.contract_id     , CASE commission.type -- or wherever this comes from         WHEN 'PRINCIPAL'         THEN SUM(whatever) OVER (PARTITION BY loan.contract_id, loan.type) -- total_whatever         ELSE SUM(something_else) OVER (PARTITION BY loan.contract_id, loan.type) -- total_something_else      END AS whatever_something  FROM loan_table loan  INNER   JOIN commission_table commission    ON loan.contract_id = commission.commission_id

Note that if your analytic functions don't have PARTITION BY contract_id you won't be able to use an index on that contract_id column at all.

Take a look at this db fiddle (you'll have to click on ... on the last result table to expand the results). Here, the loan table has an indexed (PK) contract_id column, but also some_other_id that is also unique, but not indexed and the predicate on the outer query is still on contract_id. If you compare plans for partition by contract and partition by other id, you'll see that index is not used at all in the partition by other id plan: there's a TABLE ACCESS with FULL options on the loan table, as compared to INDEX - UNIQUE SCAN in partition by contract. That's obviously because the optimizer cannot resolve the relation between contract_id and some_other_id by its own, and so it'll need to run SUM or AVG over the entire window instead of limiting window row counts through index usage.

What you can also try - if you have a dimension table with those contracts - is to join it to your results and expose the contract_id from the dimension table instead of the most likely huge loan fact table. Sometimes this can lead to an improvement in cardinality estimates through the usage of a unique index on the dimension table.

Again, it's really hard to optimize a black box, without a query or even a plan, so we don't know what's going on. CTE or a subquery can get materialized unnecessarily for example.


Thanks for the update to include an example of the column list.

Given your updated query, I would suggest changing your view (or possibly creating a second view for querying single contract_ids, if your original view could be used to query for multiple contract_ids - unless, of course, the results of the original view only make sense for individual contract_ids!) to something like:

CREATE OR REPLACE VIEW loan_vw AS WITH loan_info AS (SELECT l.*, c.* -- for future-proofing, you should list the column names explicitly; if this statement is rerun and there's a column with the same name in both tables, it'll fail.                   FROM   loan_table l                          INNER JOIN commission_table c ON l.contract_id = c.commission_id -- you should always alias the join condition columns for ease of maintenance.                  )SELECT value_date,     item,     credit_entry,     debit_entry,     GREATEST(0,            LEAST(credit_entry,                NVL(SUM(debit_entry) OVER (PARTITION BY contract_id), 0)                  - NVL(SUM(credit_entry) OVER (PARTITION BY contract_id ORDER BY value_date ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0))) AS item_paidFROM   loan_infoWHERE  TYPE <> 'PRINCIPAL'UNION ALLSELECT ...FROM   loan_infoWHERE  TYPE = 'PRINCIPAL';

Note that I've converted your join into ANSI syntax, because it's easier to understand than the old style joins (easier to separate join conditions from predicates, for a start!).