簡體   English   中英

如何讓 DB2 每次執行都重新評估查詢計划?

[英]How to make DB2 re-evaluate the query plan each execution?

我有以下 DB2 存儲過程,它允許用戶指定可變數量的參數(為他們不想提供的參數指定 NULL),其中每個參數都可用於過濾 WHERE 子句中的記錄。

CREATE PROCEDURE XX.DUMMY(IN I_FIRST_NAME VARCHAR(32),
                            I_LAST_NAME VARCHAR(100),
                            I_COMPANY_NAME VARCHAR(100))
SPECIFIC XX.DUMMY
RESULT SETS 1
MODIFIES SQL DATA
NOT DETERMINISTIC
NULL CALL
LANGUAGE SQL EXTERNAL ACTION
INHERIT SPECIAL REGISTERS
BEGIN
    DECLARE RESULTS CURSOR WITH RETURN TO CLIENT FOR
        WITH RELEVANT_RECORDS (RECORD_ID) AS (
            SELECT RECORD_ID
            FROM XX.RECORDS R
            WHERE (I_FIRST_NAME IS NULL OR R.SEARCH_FIRST_NAME = I_FIRST_NAME)
            AND (I_LAST_NAME IS NULL OR R.SEARCH_LAST_NAME = I_LAST_NAME)
            AND (I_COMPANY_NAME IS NULL OR R.SEARCH_COMPANY_NAME = I_COMPANY_NAME)
            WITH UR
        )
        SELECT RR.RECORD_ID, RD.*
        FROM RELEVANT_RECORDS RR
        JOIN XX.RECORD_DETAILS RD ON RD.RECORD_ID = RR.RECORD_ID
        FOR READ ONLY WITH UR;
    OPEN RESULTS;
END
;

據我了解,此過程將執行得很差,因為該過程的查詢計划只會在該過程被綁定(或第一次運行?)時進行評估,如果在以后的執行中指定了不同的參數組合,原始查詢計划將是使用,導致使用錯誤的計划/索引(從而使 proc 非常慢)。

我讀到 SQL 服務器允許您在過程的定義中指定OPTION(RECOMPILE)以告訴服務器為每次執行過程重新評估查詢計划,但是由於我使用的是 DB2 我正在尋找DB2 等效。

DB2 中是否有一種方法可以告訴存儲過程在每次執行時重新評估其查詢計划? 在運行任何語句之前,我是否必須在我的過程中使用SET CURRENT QUERY OPTIMIZATION之類的東西?

在您的例程中使用動態 SQL(而不是 static SQL)是確保每次編譯查詢的一種方法(雖然它與以前的實例不同,或者不在包緩存等中)。

在這種情況下,在您的例程中,您可以在例程中動態創建的查詢上使用 EXECUTE IMMEDIATE,或者使用 PREPARE 和 EXECUTE。 在這兩種情況下,完整的查詢直到執行前的運行時才存在。 因此,您構建的 SQL 字符串每次都可能不同,包括(用於執行)每次不同數量的參數標記( ? )和匹配數量的綁定參數。 SQL 查詢本身不是 static - 每次過程運行時它都可能不同 - 隨存儲過程的(已清理)參數輸入而變化。

對於靜態 SQL,請查看存儲過程SET_ROUTINE_OPTS ,其中REOPT ALWAYS需要在構建時完成,或者您可以使用注冊表變量DB2_SQLROUTINE_PREPOPTS全局提供此指令(通常這是不明智的)。 您(或 DBA)也可以安排重新綁定與 sproc 對應的 static package,始終使用 REOPT。

你可以試試DMBS_SQL模塊。
對於這樣一個簡單的目的,它看起來有點冗長,但它確實有效。
最終的 SELECT 語句是根據每個參數的可空性構造的。
XML 局部變量用於累積獲取的行以最終從 SP 返回。 您可能會使用一些 [C|G]GTT 或 ARRAY 變量來代替這些 XML 變量。

--#SET TERMINATOR @

-- Dynamic number of parameters demo
CREATE OR REPLACE PROCEDURE TEST_DBMS_SQL
(
  P_TABSCHEMA VARCHAR(128)
, P_TABNAME   VARCHAR(128)
)
DYNAMIC RESULT SETS 1
BEGIN
  DECLARE curid       INTEGER;
  DECLARE v_sql       VARCHAR(1000);
  DECLARE v_tabschema VARCHAR(128);
  DECLARE v_tabname   VARCHAR(128);
  DECLARE v_status    INTEGER;
  DECLARE v_doc       XML;
  DECLARE v_node      XML;
  
  DECLARE C1 CURSOR WITH RETURN FOR
  SELECT TABSCHEMA, TABNAME
  FROM XMLTABLE 
  (
    '$D/NODE' PASSING v_doc AS "D"
    COLUMNS
      TABSCHEMA VARCHAR(128) PATH 'TABSCHEMA'
    , TABNAME   VARCHAR(128) PATH 'TABNAME'
  );
  
  SET v_doc = XMLELEMENT(NAME "DOC");
  SET v_sql = 'SELECT TABSCHEMA, TABNAME FROM SYSCAT.TABLES WHERE 1=1';
  IF P_TABSCHEMA IS NOT NULL THEN
    SET v_sql = v_sql || ' AND TABSCHEMA = :p_tabschema'; 
  END IF;
  IF P_TABNAME IS NOT NULL THEN 
    SET v_sql = v_sql || ' AND TABNAME   = :p_tabname'; 
  END IF;

  CALL DBMS_SQL.OPEN_CURSOR(curid);
  CALL DBMS_SQL.PARSE(curid, v_sql, DBMS_SQL.native);
  
  IF P_TABSCHEMA IS NOT NULL THEN 
    CALL DBMS_SQL.BIND_VARIABLE_VARCHAR(curid, ':p_tabschema', P_TABSCHEMA);
  END IF;
  IF P_TABNAME IS NOT NULL THEN 
    CALL DBMS_SQL.BIND_VARIABLE_VARCHAR(curid, ':p_tabname', P_TABNAME);
  END IF;

  CALL DBMS_SQL.DEFINE_COLUMN_VARCHAR(curid, 1, v_tabschema, 128);
  CALL DBMS_SQL.DEFINE_COLUMN_VARCHAR(curid, 2, v_tabname, 128);

  CALL DBMS_SQL.EXECUTE(curid, v_status);
  --CALL DBMS_OUTPUT.PUT_LINE('Execute: ' || v_status);
  
  FETCH_LOOP: LOOP
  
    CALL DBMS_SQL.FETCH_ROWS(curid, v_status);
    IF v_status = 0 THEN LEAVE FETCH_LOOP; END IF;
    --CALL DBMS_OUTPUT.PUT_LINE('Fetch: ' || v_status);
    CALL DBMS_SQL.COLUMN_VALUE_VARCHAR(curid, 1, v_tabschema);
    CALL DBMS_SQL.COLUMN_VALUE_VARCHAR(curid, 2, v_tabname);
    --CALL DBMS_OUTPUT.PUT_LINE('TABSHEMA: ' || coalesce(v_tabschema, '*'));
    --CALL DBMS_OUTPUT.PUT_LINE('TABNAME: ' || coalesce(v_tabname, '*'));
    SET v_node = XMLELEMENT
    (
        NAME "NODE"
      , XMLELEMENT(NAME "TABSCHEMA", v_tabschema)
      , XMLELEMENT(NAME "TABNAME", v_tabname)
    );
  
    SET v_doc = XMLQUERY
    (
      'transform copy $mydoc := $doc modify do insert $node as last into $mydoc return $mydoc'
      passing v_doc as "doc", v_node as "node"
    );
  
  END LOOP FETCH_LOOP;
  
  --CALL DBMS_OUTPUT.PUT_LINE(XMLSERIALIZE(v_doc AS CLOB(10K)));
  CALL DBMS_SQL.CLOSE_CURSOR(curid);
  
  OPEN C1;
END@  

用法:

call test_dbms_sql(NULL, 'TABLES')@
call test_dbms_sql('SYSCAT', 'TABLES')@
call test_dbms_sql('SYSCAT', NULL)@
call test_dbms_sql(NULL, NULL)@

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM