Search code examples
sqloracle-databaseoracle10gindexingcbo

Oracle MAX() bug with NULLs CBO index fix leads to index limitation


Update 1/31/2011

I think I'm running against a DB limit. The GROUP BY expression
and all nondistinct aggregate functions may have exceeded a single
database block.
See http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/limits003.htm

Original post:

This is on Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod.

The following insert does not return the true MAX() values
if I have two records where the matchKey is the same, one
with a contributionP NULL value and another with a non-NULL value.
Sometimes, the value returned by MAX() is the NULL value.

INSERT /*+ APPEND */ INTO meCostingXPrePre(  
   matchKey                    ,  
   contributionP               ,  
   stimulusContributionP       ,  
   contributionC               ,  
   ageMultiplier               ,  
   rateTableIdP                ,  
   rateTableIdC                ,  
   accountNbrP                 ,  
   accountNbrC                 ,  
   commissionExpenseAccount    ,  
   commissionReceivableAccount ,  
   commissionType              ,  
   commission                  ,  
   pmPm                        ,  
   fee                         ,  
   planAgeGroupIdP             ,  
   planAgeGroupIdC             ,  
   rafP                        ,  
   rafC                        ,  
   nbrEmployeesRafP            ,  
   nbrEmployeesRafC            ,  
   contractId                  ,  
   basePlanId                  ,  
   groupOrPolicyNumber         ,  
   planCoverageDescription     ,  
   cobraGopn                   ,  
   cobraPcd                    ,  
   cobraCid                    ,  
   benefitId                   ,  
   insuranceStart              ,  
   insuranceEnd                ,  
   categoryId                  )  
   SELECT   
      matchKey                                                                    as matchKey                    ,  
      MAX(NVL(contributionP               ,0 ))                                   as contributionP               ,  
      MAX(NVL(stimulusContributionP       ,0 ))                                   as stimulusContributionP       ,  
      MAX(NVL(contributionC               ,0 ))                                   as contributionC               ,  
      MAX(NVL(ageMultiplier               ,0 ))                                   as ageMultiplier               ,  
      MAX(NVL(rateTableIdP                ,0 ))                                   as rateTableIdP                ,  
      MAX(NVL(rateTableIdC                ,0 ))                                   as rateTableIdC                ,  
      MAX(NVL(accountNbrP                 ,0 ))                                   as accountNbrP                 ,  
      MAX(NVL(accountNbrC                 ,0 ))                                   as accountNbrC                 ,  
      MAX(NVL(commissionExpenseAccount    ,0 ))                                   as commissionExpenseAccount    ,  
      MAX(NVL(commissionReceivableAccount ,0 ))                                   as commissionReceivableAccount ,  
      MAX(NVL(commissionType              ,0 ))                                   as commissionType              ,  
      MAX(NVL(commission                  ,0 ))                                   as commission                  ,  
      MAX(NVL(pmPm                        ,0 ))                                   as pmPm                        ,  
      MAX(NVL(fee                         ,0 ))                                   as fee                         ,  
      MAX(NVL(planAgeGroupIdP             ,0 ))                                   as planAgeGroupIdP             ,  
      MAX(NVL(planAgeGroupIdC             ,0 ))                                   as planAgeGroupIdC             ,  
      MAX(NVL(rafP                        ,0 ))                                   as rafP                        ,  
      MAX(NVL(rafC                        ,0 ))                                   as rafC                        ,  
      MAX(NVL(nbrEmployeesRafP            ,0 ))                                   as nbrEmployeesRafP            ,  
      MAX(NVL(nbrEmployeesRafC            ,0 ))                                   as nbrEmployeesRafC            ,  
      CASE WHEN MAX(contractId) IS NOT NULL AND  
                MIN(contractId) IS NOT NULL AND  
                MAX(contractId) != MIN(contractId) THEN  
         CASE WHEN MAX(contractId) = 'No Contract No' THEN  
            MIN(contractId)  
         WHEN MIN(contractId) = 'No Contract No' THEN  
            MAX(contractId)  
         ELSE  
            MAX(contractId)  
         END  
      ELSE  
         MAX(contractId)  
      END                                                                         as contractId                  ,  
      MAX(NVL(basePlanId                  ,0 ))                                   as basePlanId                  ,  
      CASE WHEN MAX(groupOrPolicyNumber) IS NOT NULL AND  
                MIN(groupOrPolicyNumber) IS NOT NULL AND  
                MAX(groupOrPolicyNumber) != MIN(groupOrPolicyNumber) THEN  
         CASE WHEN MAX(groupOrPolicyNumber) = 'No Contract No' THEN  
            MIN(groupOrPolicyNumber)  
         WHEN MIN(groupOrPolicyNumber) = 'No Contract No' THEN  
            MAX(groupOrPolicyNumber)  
         ELSE  
            MAX(groupOrPolicyNumber)  
         END  
      ELSE  
         MAX(groupOrPolicyNumber)  
      END                                                                         as groupOrPolicyNumber         ,  
      CASE WHEN MAX(planCoverageDescription) IS NOT NULL AND  
                MIN(planCoverageDescription) IS NOT NULL AND  
                MAX(planCoverageDescription) != MIN(planCoverageDescription) THEN  
         CASE WHEN MAX(planCoverageDescription) = 'No Contract No' THEN  
            MIN(planCoverageDescription)  
         WHEN MIN(planCoverageDescription) = 'No Contract No' THEN  
            MAX(planCoverageDescription)  
         ELSE  
            MAX(planCoverageDescription)  
         END  
      ELSE  
         MAX(planCoverageDescription)  
      END                                                                         as planCoverageDescription     ,  
      CASE WHEN MAX(cobraGopn) IS NOT NULL AND  
                MIN(cobraGopn) IS NOT NULL AND  
                MAX(cobraGopn) != MIN(cobraGopn) THEN  
         CASE WHEN MAX(cobraGopn) = 'No Contract No' THEN  
            MIN(cobraGopn)  
         WHEN MIN(cobraGopn) = 'No Contract No' THEN  
            MAX(cobraGopn)  
         ELSE  
            MAX(cobraGopn)  
         END  
      ELSE  
         MAX(cobraGopn)  
      END                                                                         as cobraGopn                   ,  
      CASE WHEN MAX(cobraPcd) IS NOT NULL AND  
                MIN(cobraPcd) IS NOT NULL AND  
                MAX(cobraPcd) != MIN(cobraPcd) THEN  
         CASE WHEN MAX(cobraPcd) = 'No Contract No' THEN  
            MIN(cobraPcd)  
         WHEN MIN(cobraPcd) = 'No Contract No' THEN  
            MAX(cobraPcd)  
         ELSE  
            MAX(cobraPcd)  
         END  
      ELSE  
         MAX(cobraPcd)  
      END                                                                         as cobraPcd                    ,  
      CASE WHEN MAX(cobraCid) IS NOT NULL AND  
                MIN(cobraCid) IS NOT NULL AND  
                MAX(cobraCid) != MIN(cobraCid) THEN  
         CASE WHEN MAX(cobraCid) = 'No Contract No' THEN  
            MIN(cobraCid)  
         WHEN MIN(cobraCid) = 'No Contract No' THEN  
            MAX(cobraCid)  
         ELSE  
            MAX(cobraCid)  
         END  
      ELSE  
         MAX(cobraCid)  
      END                                                                         as cobraCid                    ,  
      MAX(benefitId                           )                                   as benefitId                   ,  
      NULL                                                                        as insuranceStart              ,  
      NULL                                                                        as insuranceEnd                ,  
      NULL                                                                        as categoryId                    
   FROM meCostingXPrePrePre  
   GROUP BY matchKey;  

If I build an index as follows and give a CBO hint

CREATE INDEX C$MECOSTINGXPREPREPRE$MULTI0 ON MECOSTINGXPREPREPRE
(MATCHKEY, CONTRIBUTIONP, STIMULUSCONTRIBUTIONP, CONTRIBUTIONC, AGEMULTIPLIER, 
RATETABLEIDP, RATETABLEIDC, ACCOUNTNBRP, ACCOUNTNBRC, COMMISSIONEXPENSEACCOUNT, 
COMMISSIONRECEIVABLEACCOUNT, COMMISSIONTYPE, COMMISSION, PMPM, FEE, 
PLANAGEGROUPIDP, PLANAGEGROUPIDC, RAFP, RAFC, 
NBREMPLOYEESRAFP, NBREMPLOYEESRAFC, CONTRACTID, BASEPLANID, GROUPORPOLICYNUMBER, 
PLANCOVERAGEDESCRIPTION, COBRAGOPN, COBRAPCD, COBRACID, BENEFITID)
NOLOGGING
TABLESPACE INDX
NOPARALLEL;

INSERT /*+ APPEND */ INTO meCostingXPrePre(
   ...)
   SELECT /*+ INDEX(meCostingXPrePrePre c$mecostingxpreprepre$multi0) */
      ...
   FROM meCostingXPrePrePre
   GROUP BY matchKey;

This gets MAX() to behave correctly.

I need to add 15 extra columns and change the index

Small problem

CREATE INDEX C$MECOSTINGXPREPREPRE$MULTI0 ON MECOSTINGXPREPREPRE
(MATCHKEY, CONTRIBUTIONP, STIMULUSCONTRIBUTIONP, CONTRIBUTIONC, AGEMULTIPLIER, 
RATETABLEIDP, RATETABLEIDC, ACCOUNTNBRP, ACCOUNTNBRC, COMMISSIONEXPENSEACCOUNT, 
COMMISSIONRECEIVABLEACCOUNT, COMMISSIONTYPE, COMMISSION, PMPM, FEE, 
PLANAGEGROUPIDP, PLANAGEGROUPIDC, ADDRESSONEP, ADDRESSONEC, ADDRESSTWOP, 
ADDRESSTWOC, CITYP, CITYC, STATEP, STATEC, ZIPFULLP, ZIPFULLC, RAFP, RAFC, 
NBREMPLOYEESRAFP, NBREMPLOYEESRAFC, CONTRACTID, BASEPLANID, GROUPORPOLICYNUMBER, 
PLANCOVERAGEDESCRIPTION, COBRAGOPN, COBRAPCD, COBRACID, BENEFITID)
NOLOGGING
TABLESPACE INDX
NOPARALLEL;

gives
ORA-01793: maximum number of index columns is 32

First MAX() does not work with large column sets and second, the index+CBO patch
leads to index limitation.

Any suggestions?


Solution

  • OK, splitting the work in half will get me what I need:

    INSERT /*+ APPEND */ INTO meCostingXPrePreFirstHalf(  
       matchKey                    ,  
       contributionP               ,  
       stimulusContributionP       ,  
       contributionC               ,  
       ageMultiplier               ,  
       rateTableIdP                ,  
       rateTableIdC                ,  
       accountNbrP                 ,  
       accountNbrC                 ,  
       commissionExpenseAccount    ,  
       commissionReceivableAccount ,  
       commissionType              ,  
       commission                  ,  
       pmPm                        ,  
       fee                         ,  
       planAgeGroupIdP             ,  
       planAgeGroupIdC             )  
       SELECT   
          matchKey                                  as matchKey                    ,  
          MAX(NVL(contributionP               ,0 )) as contributionP               ,  
          MAX(NVL(stimulusContributionP       ,0 )) as stimulusContributionP       ,  
          MAX(NVL(contributionC               ,0 )) as contributionC               ,  
          MAX(NVL(ageMultiplier               ,0 )) as ageMultiplier               ,  
          MAX(NVL(rateTableIdP                ,0 )) as rateTableIdP                ,  
          MAX(NVL(rateTableIdC                ,0 )) as rateTableIdC                ,  
          MAX(NVL(accountNbrP                 ,0 )) as accountNbrP                 ,  
          MAX(NVL(accountNbrC                 ,0 )) as accountNbrC                 ,  
          MAX(NVL(commissionExpenseAccount    ,0 )) as commissionExpenseAccount    ,  
          MAX(NVL(commissionReceivableAccount ,0 )) as commissionReceivableAccount ,  
          MAX(NVL(commissionType              ,0 )) as commissionType              ,  
          MAX(NVL(commission                  ,0 )) as commission                  ,  
          MAX(NVL(pmPm                        ,0 )) as pmPm                        ,  
          MAX(NVL(fee                         ,0 )) as fee                         ,  
          MAX(NVL(planAgeGroupIdP             ,0 )) as planAgeGroupIdP             ,  
          MAX(NVL(planAgeGroupIdC             ,0 )) as planAgeGroupIdC                
       FROM meCostingXPrePrePre  
       GROUP BY matchKey;  
    /* No commit yet. */
    
    INSERT /*+ APPEND */ INTO meCostingXPrePreOtherHalf(  
       matchKey                    ,  
       rafP                        ,  
       rafC                        ,  
       nbrEmployeesRafP            ,  
       nbrEmployeesRafC            ,  
       contractId                  ,  
       basePlanId                  ,  
       groupOrPolicyNumber         ,  
       planCoverageDescription     ,  
       cobraGopn                   ,  
       cobraPcd                    ,  
       cobraCid                    ,  
       benefitId                   ,  
       insuranceStart              ,  
       insuranceEnd                ,  
       categoryId                  )  
       SELECT  
          matchKey                                                                    as matchKey                    ,  
          MAX(NVL(rafP                        ,0 ))                                   as rafP                        ,  
          MAX(NVL(rafC                        ,0 ))                                   as rafC                        ,  
          MAX(NVL(nbrEmployeesRafP            ,0 ))                                   as nbrEmployeesRafP            ,  
          MAX(NVL(nbrEmployeesRafC            ,0 ))                                   as nbrEmployeesRafC            ,  
          CASE WHEN MAX(contractId) IS NOT NULL AND  
                    MIN(contractId) IS NOT NULL AND  
                    MAX(contractId) != MIN(contractId) THEN  
             CASE WHEN MAX(contractId) = 'No Contract No' THEN  
                MIN(contractId)  
             WHEN MIN(contractId) = 'No Contract No' THEN  
                MAX(contractId)  
             ELSE  
                MAX(contractId)  
             END  
          ELSE  
             MAX(contractId)  
          END                                                                         as contractId                  ,  
          MAX(NVL(basePlanId                  ,0 ))                                   as basePlanId                  ,  
          CASE WHEN MAX(groupOrPolicyNumber) IS NOT NULL AND  
                    MIN(groupOrPolicyNumber) IS NOT NULL AND  
                    MAX(groupOrPolicyNumber) != MIN(groupOrPolicyNumber) THEN  
             CASE WHEN MAX(groupOrPolicyNumber) = 'No Contract No' THEN  
                MIN(groupOrPolicyNumber)  
             WHEN MIN(groupOrPolicyNumber) = 'No Contract No' THEN  
                MAX(groupOrPolicyNumber)  
             ELSE  
                MAX(groupOrPolicyNumber)  
             END  
          ELSE  
             MAX(groupOrPolicyNumber)  
          END                                                                         as groupOrPolicyNumber         ,  
          CASE WHEN MAX(planCoverageDescription) IS NOT NULL AND  
                    MIN(planCoverageDescription) IS NOT NULL AND  
                    MAX(planCoverageDescription) != MIN(planCoverageDescription) THEN  
             CASE WHEN MAX(planCoverageDescription) = 'No Contract No' THEN  
                MIN(planCoverageDescription)  
             WHEN MIN(planCoverageDescription) = 'No Contract No' THEN  
                MAX(planCoverageDescription)  
             ELSE  
                MAX(planCoverageDescription)  
             END  
          ELSE  
             MAX(planCoverageDescription)  
          END                                                                         as planCoverageDescription     ,  
          CASE WHEN MAX(cobraGopn) IS NOT NULL AND  
                    MIN(cobraGopn) IS NOT NULL AND  
                    MAX(cobraGopn) != MIN(cobraGopn) THEN  
             CASE WHEN MAX(cobraGopn) = 'No Contract No' THEN  
                MIN(cobraGopn)  
             WHEN MIN(cobraGopn) = 'No Contract No' THEN  
                MAX(cobraGopn)  
             ELSE  
                MAX(cobraGopn)  
             END  
          ELSE  
             MAX(cobraGopn)  
          END                                                                         as cobraGopn                   ,  
          CASE WHEN MAX(cobraPcd) IS NOT NULL AND  
                    MIN(cobraPcd) IS NOT NULL AND  
                    MAX(cobraPcd) != MIN(cobraPcd) THEN  
             CASE WHEN MAX(cobraPcd) = 'No Contract No' THEN  
                MIN(cobraPcd)  
             WHEN MIN(cobraPcd) = 'No Contract No' THEN  
                MAX(cobraPcd)  
             ELSE  
                MAX(cobraPcd)  
             END  
          ELSE  
             MAX(cobraPcd)  
          END                                                                         as cobraPcd                    ,  
          CASE WHEN MAX(cobraCid) IS NOT NULL AND  
                    MIN(cobraCid) IS NOT NULL AND  
                    MAX(cobraCid) != MIN(cobraCid) THEN  
             CASE WHEN MAX(cobraCid) = 'No Contract No' THEN  
                MIN(cobraCid)  
             WHEN MIN(cobraCid) = 'No Contract No' THEN  
                MAX(cobraCid)  
             ELSE  
                MAX(cobraCid)  
             END  
          ELSE  
             MAX(cobraCid)  
          END                                                                         as cobraCid                    ,  
          MAX(benefitId                           )                                   as benefitId                      
       FROM meCostingXPrePrePre  
       GROUP BY matchKey;  
    /* No commit yet. */
    
    INSERT /*+ APPEND */ INTO meCostingXPrePre(  
       matchKey                    ,  
       contributionP               ,  
       stimulusContributionP       ,  
       contributionC               ,  
       ageMultiplier               ,  
       rateTableIdP                ,  
       rateTableIdC                ,  
       accountNbrP                 ,  
       accountNbrC                 ,  
       commissionExpenseAccount    ,  
       commissionReceivableAccount ,  
       commissionType              ,  
       commission                  ,  
       pmPm                        ,  
       fee                         ,  
       planAgeGroupIdP             ,  
       planAgeGroupIdC             ,  
       rafP                        ,  
       rafC                        ,  
       nbrEmployeesRafP            ,  
       nbrEmployeesRafC            ,  
       contractId                  ,  
       basePlanId                  ,  
       groupOrPolicyNumber         ,  
       planCoverageDescription     ,  
       cobraGopn                   ,  
       cobraPcd                    ,  
       cobraCid                    ,  
       benefitId                   ,  
       insuranceStart              ,  
       insuranceEnd                ,  
       categoryId                  )  
       SELECT 
          f.matchKey                    as matchKey                    ,  
          f.contributionP               as contributionP               ,  
          f.stimulusContributionP       as stimulusContributionP       ,  
          f.contributionC               as contributionC               ,  
          f.ageMultiplier               as ageMultiplier               ,  
          f.rateTableIdP                as rateTableIdP                ,  
          f.rateTableIdC                as rateTableIdC                ,  
          f.accountNbrP                 as accountNbrP                 ,  
          f.accountNbrC                 as accountNbrC                 ,  
          f.commissionExpenseAccount    as commissionExpenseAccount    ,  
          f.commissionReceivableAccount as commissionReceivableAccount ,  
          f.commissionType              as commissionType              ,  
          f.commission                  as commission                  ,  
          f.pmPm                        as pmPm                        ,  
          f.fee                         as fee                         ,  
          f.planAgeGroupIdP             as planAgeGroupIdP             ,  
          f.planAgeGroupIdC             as planAgeGroupIdC             ,  
          o.rafP                        as rafP                        ,  
          o.rafC                        as rafC                        ,  
          o.nbrEmployeesRafP            as nbrEmployeesRafP            ,  
          o.nbrEmployeesRafC            as nbrEmployeesRafC            ,  
          o.contractId                  as contractId                  ,  
          o.basePlanId                  as basePlanId                  ,  
          o.groupOrPolicyNumber         as groupOrPolicyNumber         ,  
          o.planCoverageDescription     as planCoverageDescription     ,  
          o.cobraGopn                   as cobraGopn                   ,  
          o.cobraPcd                    as cobraPcd                    ,  
          o.cobraCid                    as cobraCid                    ,  
          o.benefitId                   as benefitId                   ,  
          o.insuranceStart              as insuranceStart              ,  
          o.insuranceEnd                as insuranceEnd                ,  
          o.categoryId                  as categoryId                  ,  
          NULL                          as insuranceStart              ,  
          NULL                          as insuranceEnd                ,  
          NULL                          as categoryId                     
       FROM  
          meCostingXPrePreFirstHalf f  
             INNER JOIN meCostingXPrePreOtherHalf o  
             ON f.matchKey = o.matchKey;  
    /* Now it is safe to commit. */
    COMMIT;