0
SELECT t1.member_id ,
  SUM(t1.paid_amt) AS paid_amt
FROM
  (SELECT DISTINCT fm.member_id,
    fc.claim_skey_no,
    fc.claim_id,
    fc.claim_line_no ,
    CASE
      WHEN fc.claim_type_cd = 'RX'
      THEN NVL(fc.rx_paid_amt,0) -- For RX claims use rx_paid_amt as paid amount
      ELSE NVL(fc.approved_amt,0)
    END AS paid_amt -- For all other claims use approved_amt
    ,
    CASE
      WHEN fc.claim_type_cd = 'RX'
      THEN fc.submit_dt --For RX claims use submit_dt as paid date
      ELSE NVL(fc.paid_dt,NVL(fc.edi_eob_dt,NVL(fc.eob_run_dt,fc.outsource_vndr_paid_dt)))
    END AS paid_dt --For all other claims use paid_dt
  FROM dwprod.fct_claim fc ,
    dwprod.fct_member fm
  WHERE fc.mbr_skey_no = fm.member_skey_no
    --  The service_from_dt on the claim must be between the reimbursement time period.
  AND fc.service_from_dt BETWEEN '31-MAY-2013' AND '30-Jun-2014'
    -- The follwong 2 conditions make sure that the calims selected are final-status (unadjusted)
    -- For non-RX claims, the adjust_type_cd must be Null and the dw_backout_tag must be Null or 'N'
    -- For RX claims only the dw_backout_tag must be Null or 'N', the adjust_type_cd is ignored
  AND
    CASE
      WHEN fc.claim_type_cd = 'RX'
      THEN 1
      WHEN fc.claim_type_cd <> 'RX'
      AND fc.adjust_type_cd IS NULL
      THEN 1
      ELSE 0
    END                          = 1
  AND NVL(fc.dw_backout_tag,'N') = 'N'
    -- The claim must be in an 'Approved' status, indicated by a status_type_cd = 'A'
  AND fc.status_type_cd = 'A'
    --  QNXT claims must be in a 'PAID' status
    --  Non QNXT claims in the warehouse are assumed to be paid - There are no pended RX claims.
  AND
    CASE
      WHEN fc.dw_source_cd <> 'QNXT'
      THEN 1
      WHEN fc.dw_source_cd  = 'QNXT'
      AND fc.last_status_nm = 'PAID'
      THEN 1
      ELSE 0
    END = 1
    -- Dental claims are excluded
  AND fc.dw_source_cd <> 'DBP'
    -- Excludes any Medicare Non-RCI claims
  AND
    CASE
      WHEN NVL(fc.program_nm,'OTHER')    = 'MEDICAID'
      AND NVL(fc.enroll_ratecode,'RCI') IN ('RCII','RCV','RCVII')
      THEN 0
      ELSE 1
    END = 0
    -- It Fits! claims are excluded
  AND NVL(fc.expense_cat_nm,'Other')  <> 'FITNESS'
  AND NVL(fc.proc1_skey_no,12345) NOT IN (21586,21588,21589)
    --
  AND
    CASE
      WHEN NVL(fc.program_nm,'OTHER')    = 'MEDICAID'
      AND NVL(fc.enroll_ratecode,'RCI') IN ('RCII','RCV','RCVII')
      THEN 1
      WHEN EXISTS
        (SELECT 1
        FROM dwprod.fct_member_enroll me
        WHERE fm.member_skey_no = me.mbr_skey_no
        AND fc.service_from_dt BETWEEN me.segment_effect_dt AND me.segment_term_dt
        AND me.program_nm       = 'MEDICAID'
        AND me.enroll_ratecode IN ('RCII','RCV','RCVII')
        )
      THEN 1
      ELSE 0
    END = 1
  ) t1
  --Where t1.paid_dt < '31-JAN-2014'
GROUP BY t1.member_id
HAVING SUM(t1.paid_amt) > 175000
Maheswaran Ravisankar
  • 17,652
  • 6
  • 47
  • 69
  • I tried using this (FROM dwprod.fct_claim fc , INNER JOIN dwprod.fct_member fm ON fc.mbr_skey_no = fm.member_skey_no) still taking long time. – user3357343 Feb 26 '14 at 21:12
  • 1
    Have you checked the EXPLAIN PLAN yet? If not, why not? – Edmund Schweppe Feb 26 '14 at 23:47
  • Though it is not much on your question, `AND FC.SERVICE_FROM_DT BETWEEN '31-MAY-2013' AND '30-JUN-2014'` should have been `AND FC.SERVICE_FROM_DT BETWEEN TO_DATE('31-MAY-2013','DD-MON-YYYY') AND TO_DATE('30-JUN-2014','DD-MON-YYYY')` – Srini V Feb 27 '14 at 05:51

3 Answers3

5

Run an explain plan to see what's causing the slowdown. From the top of my head, this is what's 'killing' you:

WHEN EXISTS
        (SELECT 1
        FROM dwprod.fct_member_enroll me
        WHERE fm.member_skey_no = me.mbr_skey_no
        AND fc.service_from_dt BETWEEN me.segment_effect_dt AND me.segment_term_dt
        AND me.program_nm       = 'MEDICAID'
        AND me.enroll_ratecode IN ('RCII','RCV','RCVII')
        )

See if you can somehow change this exists logic to something with better performance. The explain plan is a must though!

Koshera
  • 439
  • 5
  • 14
  • 2
    I agree an explain plan would go a long way to help solving this performance issue. Without that, I would guess another part of the problem is that the predicates are too complicated for Oracle to correctly estimate. That leads to poor cardinality estimates, ROWS=1 where there are really millions or billions of rows returned. Which then leads to a NESTED LOOP instead of a HASH JOIN. With such complicated predicates there may not be a good way to nudge the optimizer in the right direction. This may require a hint like `/*+ use_hash(fc fm) full(fc) full (fm)*/`. – Jon Heller Feb 26 '14 at 19:38
  • I agree! After the explain plan, you can see what the optimizer does and then use Oracle hints to boost your performance. I've seen the optimizer doing quite unreasonable decisions (exactly nested loops instead of hash join) and a simple hint has improved a query from ~30 minutes to ~1 minute... – Koshera Feb 26 '14 at 19:43
2

I'm gonna make a semi-blind guess here, based on similar queries in the DW I'm working with. Oracle's optimizer gets easily confused by predicates such as:

where (case when ... then ... else ... end) = 1;

The reason is that Oracle grossly over estimate the selectivity. Check the explain plan like others have said. If you find that the estimated cardinality of table dwprod.fct_claim seems way too low, try unrolling the case statements.

For example, instead of:

  AND CASE WHEN fc.dw_source_cd <> 'QNXT' THEN 1
            WHEN fc.dw_source_cd  = 'QNXT' AND fc.last_status_nm = 'PAID' THEN 1
                                                                          ELSE 0
       END = 1

Write:

and (    fc.dw_source_cd <> 'QNXT'
     or (fc.dw_source_cd  = 'QNXT' and fc.last_status_nm = 'PAID')
    )

Final note. This seems to be less of a problem in version 11, but I have not yet had time to investigate why.

Ronnis
  • 12,593
  • 2
  • 32
  • 52
-1
FROM dwprod.fct_claim fc ,
    dwprod.fct_member fm
WHERE fc.mbr_skey_no = fm.member_skey_no

This cross join is effectively an inner join. I can't say whether Oracle will optimize this, but there's no reason not to make its job easier:

FROM dwprod.fct_claim fc ,
    INNER JOIN dwprod.fct_member fm
    ON fc.mbr_skey_no = fm.member_skey_no
Thom Smith
  • 13,916
  • 6
  • 45
  • 91
  • I prefer ANSI syntax, and believe it will improve performance in the long-run by making queries easier to understand and debug. But for this specific statement I seriously doubt it will help with performance. Oracle converts most ANSI syntax to the old-fashioned syntax anyway. – Jon Heller Feb 26 '14 at 19:34
  • I do prefer ANSI, but not in 12c since there is a open bug in using that way. Since 12c has some enhancements in outer join which differs the way the SQL is processed when compared to older versions. Look here http://stackoverflow.com/questions/19686262/query-featuring-outer-joins-behaves-differently-in-oracle-12c – Srini V Feb 27 '14 at 05:49