details: https://code.openbravo.com/erp/devel/pi/rev/0b60872f78f5 changeset: 26829:0b60872f78f5 user: Víctor Martínez Romanos <victor.martinez <at> openbravo.com> date: Wed May 20 16:30:14 2015 +0200 summary: Fixed bug 29664: Create Price List performance
The patch contains the following changes that improve the process performance (in my local environment the performance is increased an average of 80%): 1. count(*) inside M_GET_NO_TRX_PRODUCT_COST has been rewritten to use exists clause 2. Added 2 indices to M_Product_PO table on M_PRODUCT_ID and C_BPARTNER_ID columns to reduce seq. scans on this table 3. M_PRICELIST_CREATE: Force to analyze C_TEMP_Selection table in Postgres According to PostgreSQL documentation, the autovacuum daemon cannot access and therefore cannot vacuum or analyze temporary tables. So it's recommended to run ANALYZE after the table is populated to improve performance. Oracle doesn't seem to be affected by this problem, so the analyze is only executed for postgres 4. M_PRICELIST_CREATE: Complex queries that fill the C_TEMP_Selection has been rewritten using a dynamic SQL run through the EXECUTE command. Although these queries are in general fast, they are executed as many times as records to be inserted into the Price List. So, the bigger amount of records the slower the process is. It has been detected that the execution plan created for these queries can be improved a lot if we remove useless parts in the where clause (OR stuff). That's why we dynamically build the sql based on the parameters, which drastically improves the execution plan performance. 5. M_PRICELIST_CREATE: Removed useless join to ad_client table to calculate the client's currency. Instead we get it at the beginning of the process just one time. 6. M_PRICELIST_CREATE: Removed code included into the IF (v_Costbased = 'N'AND (v_PriceList_Version_Base_ID IS NULL)). This code is never executed, because there is a validation at the beginning of the process to avoid that situation. Other changes not included that could improve performance: 1. Changing the db functions ad_isorgincluded and ad_org_isinnaturaltree from VOLATILE to STABLE reduces execution time for cost based price lists. Right now the DBSM doesn't support this flag, so a feature request #29943 has been created diffstat: src-db/database/model/functions/M_GET_NO_TRX_PRODUCT_COST.xml | 26 +- src-db/database/model/functions/M_PRICELIST_CREATE.xml | 229 +++++---- src-db/database/model/tables/M_PRODUCT_PO.xml | 6 + 3 files changed, 140 insertions(+), 121 deletions(-) diffs (truncated from 387 to 300 lines): diff -r 7eae99f7fe79 -r 0b60872f78f5 src-db/database/model/functions/M_GET_NO_TRX_PRODUCT_COST.xml --- a/src-db/database/model/functions/M_GET_NO_TRX_PRODUCT_COST.xml Wed May 27 01:29:05 2015 +0530 +++ b/src-db/database/model/functions/M_GET_NO_TRX_PRODUCT_COST.xml Wed May 20 16:30:14 2015 +0200 @@ -34,7 +34,7 @@ * under the License. * The Original Code is Openbravo ERP. * The Initial Developer of the Original Code is Openbravo SLU -* All portions are Copyright (C) 2012 Openbravo SLU +* All portions are Copyright (C) 2012-2015 Openbravo SLU * All Rights Reserved. * Contributor(s): ______________________________________. ************************************************************************/ @@ -68,16 +68,20 @@ END IF; END IF; - SELECT COUNT(*) INTO v_Count - FROM m_costing - WHERE datefrom <= p_movementdate - AND dateto > p_movementdate - AND m_product_id = p_product_id - AND COALESCE(m_warehouse_id, p_warehouse_id, '-1') = COALESCE(p_warehouse_id, m_warehouse_id, '-1') - AND ad_org_id = v_legal_entity - AND cost IS NOT NULL - AND costtype IN ('STA', 'AVA') - AND costtype = COALESCE(v_CostType, costtype); + + SELECT count(*) INTO v_Count + FROM DUAL + WHERE EXISTS (SELECT 1 + FROM m_costing + WHERE datefrom <= p_movementdate + AND dateto > p_movementdate + AND m_product_id = p_product_id + AND COALESCE(m_warehouse_id, p_warehouse_id, '-1') = COALESCE(p_warehouse_id, m_warehouse_id, '-1') + AND ad_org_id = v_legal_entity + AND cost IS NOT NULL + AND costtype IN ('STA', 'AVA') + AND costtype = COALESCE(v_CostType, costtype) + ); IF(v_Count = 0) THEN RETURN NULL; END IF; diff -r 7eae99f7fe79 -r 0b60872f78f5 src-db/database/model/functions/M_PRICELIST_CREATE.xml --- a/src-db/database/model/functions/M_PRICELIST_CREATE.xml Wed May 27 01:29:05 2015 +0530 +++ b/src-db/database/model/functions/M_PRICELIST_CREATE.xml Wed May 20 16:30:14 2015 +0200 @@ -52,6 +52,7 @@ v_Costbased M_PriceList.Costbased%TYPE; v_validfromdate M_PriceList_Version.ValidFrom%TYPE; v_isCostMigrated NUMBER; + v_clientCurrencyId AD_Client.C_Currency_ID%TYPE; -- -- Get PL Parameter Cur_DiscountLine RECORD; @@ -62,6 +63,10 @@ v_user AD_USER.AD_USER_ID%TYPE; v_shline_count rowcount%TYPE; + + v_Sql_analyze_pg VARCHAR2(2000):='ANALYZE C_TEMP_Selection'; + v_Sql_insert VARCHAR2(2000); + v_rdbms VARCHAR2(2000):=AD_GET_RDBMS(); BEGIN -- Update AD_PInstance @@ -100,12 +105,31 @@ FROM DUAL WHERE EXISTS (SELECT 1 FROM ad_preference WHERE attribute = 'Cost_Eng_Ins_Migrated'); - SELECT M_PriceList_Version_Base_ID , Costbased, validfrom - INTO v_PriceList_Version_Base_ID, v_Costbased, v_validfromdate + -- Get PriceList Info + v_ResultStr:='GetPLInfo'; + DBMS_OUTPUT.PUT_LINE(v_ResultStr) ; + SELECT p.C_Currency_ID, + c.priceprecision, + v.AD_Client_ID, + v.AD_Org_ID, + v.UpdatedBy, + v.M_DiscountSchema_ID, + M_PriceList_Version_Base_ID, Costbased, validfrom, cl.c_currency_id + INTO v_Currency_ID, + v_StdPrecision, + v_Client_ID, + v_Org_ID, + v_UpdatedBy, + v_DiscountSchema_ID, + v_PriceList_Version_Base_ID, v_Costbased, v_validfromdate, v_clientCurrencyId FROM M_PriceList p, - M_PriceList_Version v + M_PriceList_Version v, + C_Currency c, + AD_Client cl WHERE p.M_PriceList_ID=v.M_PriceList_ID - AND v.M_PriceList_Version_ID=v_PriceList_Version_ID; + AND p.C_Currency_ID=c.C_Currency_ID + AND cl.ad_client_id = p.ad_client_id + AND v.M_PriceList_Version_ID=v_PriceList_Version_ID; IF (v_Costbased = 'N' AND (v_PriceList_Version_Base_ID IS NULL OR v_PriceList_Version_Base_ID='')) THEN RAISE_APPLICATION_ERROR(-20000, '@BasePriceListRequired@'); END IF; @@ -201,29 +225,6 @@ v_Message:='@Deleted@=' || rowcount || ' - '; DBMS_OUTPUT.PUT_LINE(v_Message) ; END IF; - -- Get PriceList Info - v_ResultStr:='GetPLInfo'; - DBMS_OUTPUT.PUT_LINE(v_ResultStr) ; - SELECT p.C_Currency_ID, - c.priceprecision, - v.AD_Client_ID, - v.AD_Org_ID, - v.UpdatedBy, - v.M_DiscountSchema_ID, - M_PriceList_Version_Base_ID - INTO v_Currency_ID, - v_StdPrecision, - v_Client_ID, - v_Org_ID, - v_UpdatedBy, - v_DiscountSchema_ID, - v_PriceList_Version_Base_ID - FROM M_PriceList p, - M_PriceList_Version v, - C_Currency c - WHERE p.M_PriceList_ID=v.M_PriceList_ID - AND p.C_Currency_ID=c.C_Currency_ID - AND v.M_PriceList_Version_ID=v_PriceList_Version_ID; /** * For All Discount Lines in Sequence */ @@ -243,80 +244,90 @@ -- ----------------------------------- -- Create Selection in temporary table -- ----------------------------------- - IF(v_Costbased = 'N'AND (v_PriceList_Version_Base_ID IS NULL)) THEN - -- Create Selection from M_Product_PO - INSERT - INTO C_TEMP_Selection - ( - C_TEMP_Selection_ID - ) - SELECT DISTINCT po.M_Product_ID - FROM M_Product p, - M_Product_PO po - WHERE p.M_Product_ID=po.M_Product_ID - AND(p.AD_Client_ID=v_Client_ID - OR p.AD_Client_ID='0') - AND p.IsActive='Y' - AND po.IsActive='Y' - AND po.IsCurrentVendor='Y' -- Optional Restrictions - AND(Cur_DiscountLine.M_Product_Category_ID IS NULL - OR p.M_Product_Category_ID=Cur_DiscountLine.M_Product_Category_ID) - AND(Cur_DiscountLine.C_BPartner_ID IS NULL - OR po.C_BPartner_ID=Cur_DiscountLine.C_BPartner_ID) - AND(Cur_DiscountLine.M_Product_ID IS NULL - OR p.M_Product_ID=Cur_DiscountLine.M_Product_ID) ; - - ELSIF (v_Costbased = 'Y' AND (v_PriceList_Version_Base_ID IS NULL OR v_PriceList_Version_Base_ID='')) THEN - INSERT - INTO C_TEMP_Selection - ( - C_TEMP_Selection_ID - ) - SELECT DISTINCT p.M_Product_id - FROM M_Product p - inner join M_costing co on p.M_Product_ID=co.M_Product_ID - WHERE (p.AD_Client_ID=v_Client_ID - OR p.AD_Client_ID='0') - AND p.IsActive='Y' - AND AD_ORG_ISINNATURALTREE(v_org_id,co.ad_org_id,v_Client_ID)='Y' - AND(Cur_DiscountLine.M_Product_Category_ID IS NULL - OR p.M_Product_Category_ID=Cur_DiscountLine.M_Product_Category_ID) - AND(Cur_DiscountLine.C_BPartner_ID IS NULL - OR p.C_BPartner_ID=Cur_DiscountLine.C_BPartner_ID) - AND(Cur_DiscountLine.M_Product_ID IS NULL - OR p.M_Product_ID=Cur_DiscountLine.M_Product_ID) - AND TRUNC(datefrom)<=v_validfromdate AND TRUNC(dateto)>v_validfromdate - AND NOT EXISTS - (select M_Product_id from c_discount d where co.M_Product_id=d.M_Product_id); + IF (v_Costbased = 'Y' AND (v_PriceList_Version_Base_ID IS NULL OR v_PriceList_Version_Base_ID='')) THEN + v_Sql_insert := ' + INSERT + INTO C_TEMP_Selection + ( + C_TEMP_Selection_ID + ) + SELECT DISTINCT p.M_Product_id + FROM M_Product p + inner join M_costing co on p.M_Product_ID=co.M_Product_ID + WHERE p.AD_Client_ID in (''0'', ''' || v_Client_ID || ''') + AND p.IsActive=''Y'' + AND AD_ORG_ISINNATURALTREE(''' || v_org_id || ''',co.ad_org_id,''' || v_Client_ID || ''')=''Y'' + AND NOT EXISTS (select 1 from c_discount d where co.M_Product_id=d.M_Product_id) '; + + IF (v_rdbms = 'POSTGRE') THEN + v_Sql_insert := v_Sql_insert || ' + AND TRUNC(datefrom)<= $1 AND TRUNC(dateto)> $2 '; + ELSE + v_Sql_insert := v_Sql_insert || ' + AND TRUNC(datefrom)<= :1 AND TRUNC(dateto)> :2 '; + END IF; + IF (Cur_DiscountLine.M_Product_Category_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND p.M_Product_Category_ID=''' || Cur_DiscountLine.M_Product_Category_ID || ''' '; + END IF; + IF (Cur_DiscountLine.C_BPartner_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND p.C_BPartner_ID=''' || Cur_DiscountLine.C_BPartner_ID || ''' '; + END IF; + IF (Cur_DiscountLine.M_Product_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND p.M_Product_ID= ''' || Cur_DiscountLine.M_Product_ID || ''' '; + END IF; + -- Force the execution this way to improve query performance + EXECUTE IMMEDIATE v_Sql_insert USING v_validfromdate, v_validfromdate; + ELSE -- Create Selection from existing PriceList - INSERT - INTO C_TEMP_Selection - ( - C_TEMP_Selection_ID - ) - SELECT DISTINCT p.M_Product_ID - FROM M_Product p, - M_ProductPrice pp - WHERE p.M_Product_ID=pp.M_Product_ID - AND pp.M_PriceList_Version_ID=v_PriceList_Version_Base_ID - AND p.IsActive='Y' - AND pp.IsActive='Y' -- Optional Restrictions - AND(Cur_DiscountLine.M_Product_Category_ID IS NULL - OR p.M_Product_Category_ID=Cur_DiscountLine.M_Product_Category_ID) - AND(Cur_DiscountLine.C_BPartner_ID IS NULL - OR EXISTS - (SELECT * - FROM M_Product_PO po - WHERE po.M_Product_ID=p.M_Product_ID - AND po.C_BPartner_ID=Cur_DiscountLine.C_BPartner_ID - )) - AND(Cur_DiscountLine.M_Product_ID IS NULL - OR p.M_Product_ID=Cur_DiscountLine.M_Product_ID) ; + v_Sql_insert := ' + INSERT + INTO C_TEMP_Selection + ( + C_TEMP_Selection_ID + ) + + SELECT p.M_Product_ID + FROM M_Product p, + M_ProductPrice pp + WHERE p.M_Product_ID=pp.M_Product_ID + AND pp.M_PriceList_Version_ID= ''' || v_PriceList_Version_Base_ID || ''' + AND p.IsActive=''Y'' + AND pp.IsActive=''Y'' '; + + IF (Cur_DiscountLine.M_Product_Category_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND p.M_Product_Category_ID=''' || Cur_DiscountLine.M_Product_Category_ID || ''' '; + END IF; + IF (Cur_DiscountLine.C_BPartner_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND EXISTS + (SELECT 1 + FROM M_Product_PO po + WHERE po.M_Product_ID=p.M_Product_ID + AND po.C_BPartner_ID=''' || Cur_DiscountLine.C_BPartner_ID ||''') '; + END IF; + IF (Cur_DiscountLine.M_Product_ID IS NOT NULL) THEN + v_Sql_insert := v_Sql_insert || ' + AND p.M_Product_ID= ''' || Cur_DiscountLine.M_Product_ID || ''' '; + END IF; + -- Force the execution this way to improve query performance + EXECUTE IMMEDIATE v_Sql_insert; + END IF; + rowcount:=SQL%ROWCOUNT; v_Message:=v_Message || '@Selected@=' || rowcount; -- DBMS_OUTPUT.PUT_LINE(v_Message); + + -- Temporary tables are not accessed by the autovacuum daemon, so we force an analyze to calculate index + IF (v_rdbms = 'POSTGRE') THEN + EXECUTE IMMEDIATE v_Sql_analyze_pg; + END IF; + -- Delete Prices in Selection, so that we can insert IF(v_PriceList_Version_Base_ID IS NULL OR v_PriceList_Version_Base_ID<>v_PriceList_Version_ID) THEN v_ResultStr:=v_ResultStr || ', Delete'; @@ -324,7 +335,7 @@ FROM M_ProductPrice WHERE M_ProductPrice.M_PriceList_Version_ID=v_PriceList_Version_ID AND EXISTS - (SELECT * + (SELECT 1 FROM C_TEMP_Selection s WHERE M_ProductPrice.M_Product_ID=s.C_TEMP_Selection_ID ) @@ -362,7 +373,7 @@ ------------------------------------------------------------------------------ _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits