ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Native Dynamic SQL and Dynamic PL/SQL(4)

Native Dynamic SQL and Dynamic PL/SQL(4)

原创 Linux操作系统 作者:susie0409 时间:2010-03-02 22:22:00 0 删除 编辑
Dynamic SELECTS

Add a Note HereQueries may return a single row or multiple rows. In this section, I discuss the techniques for performing single-row and multirow SELECTS with dynamic SQL using the tables previously created.

Add a Note HereSingle-Row SELECTS

Add a Note HereThe statement

Add a Note Here
EXECUTE IMMEDIATE  INTO 
USING ;

Add a Note Hereis used to execute dynamic single-row SELECTS.

Add a Note HereSuppose you want to update the order_tab information with the latest total quantity and total price for a given order for all possible regions. In this case, you require a dynamic SELECT that returns a single row, the summation of quantity and price for an input order_id.

Add a Note HereThe procedure update_dyn_table uses a dynamic SELECT that returns a single row. Here's the code for this procedure:

Add a Note Here
Create or replace Procedure update_dyn_table
                                              (i_region_name VARCHAR2,
                                               i_order_id NUMBER,
                                               retcd OUT NUMBER,
                                               errmsg OUT VARCHAR2)
authid current_user
Is
  Dyn_upd_string1 VARCHAR2(1000);
  Dyn_query_string VARCHAR2(1000);
  Dyn_Table_name1 VARCHAR2(30);
  Dyn_Table_name2 VARCHAR2(30);
  V_total_price NUMBER;
  V_total_quantity NUMBER;
Begin
  Dyn_table_name1 := 'ORDER_ITEMS_FOR_'||replace(trim(i_region_name),' ','_');
  Dyn_table_name2 := 'ORDERS_FOR_'||i_region_name;
  Dyn_query_string := ' SELECT SUM(quantity), SUM(unit_price*quantity) FROM '||
                                       dyn_table_name1||
                                      ' WHERE order_id = :input_order_id ';
  Dyn_upd_string1 := ' UPDATE '||dyn_table_name2||
               ' SET total_qty = :total_qty, total_price = :total_price WHERE '||
                         ' order_id = :input_order_id ';
   EXECUTE IMMEDIATE dyn_query_string INTO v_total_price, v_total_quantit
   USING i_order_id;
   EXECUTE IMMEDIATE dyn_upd_string1
     USING v_total_quantity, v_total_price, i_order_id;
   retcd := 0;
EXCEPTION WHEN OTHERS THEN
  retcd := SQLCODE;
  errmsg :='ERR:'||SQLERRM;
End;
/

Add a Note HereThis procedure does the following:

  • Add a Note HereCreates two dynamic table names concatenating the input region name with the constants ORDER_ITEMS_FOR_ and ORDERS_FOR_ and places them in two PL/SQL variables named dyn_table_name1 and dyn_table_name2. This process is for the order items table and the orders table for each region, respectively.

  • Add a Note HereCreates a dynamically generated SELECT statement using the dyn_table_name1 variable and places it in a PL/SQL variable named dyn_query_string. This process is for selecting the total quantity and total price from the ORDER_ITEMS_FOR_ table for each input order_id. It uses a bind variable called :input_order_id for the input order_id. Each SELECT returns only one row corresponding to the input order_id.

  • Add a Note HereCreates a dynamically generated UPDATE statement using the dyn_table_name2 variable and places it in a PL/SQL variable named dyn_upd_string1. This process is for updating the total_qty and total_price columns in the ORDERS_FOR_ table for each order_id. It uses a bind variable called :input_order_id for the input order_id. It also uses two more bind variables named :total_qty and :total_price for setting the values of the total quantity and total price for each order.

  • Add a Note HereExecutes these two dynamic strings using the EXECUTE IMMEDIATE statement. This statement retrieves the total quantity and total price for each order_id from the ORDER_ITEMS_FOR_ table and updates the corresponding record in the ORDERS_FOR_ table.

  • Add a Note HereOutputs a return code of 0 on success in the form. of an OUT parameter named retcd. On error it outputs the corresponding SQLCODE and SQLERRM in the form. of two OUT parameters, retcd and errmsg.

Add a Note HereMultirow SELECTS

Add a Note HereMultirow SELECTS are handled in a separate manner from single-row SELECTS. With static SQL, multirow SELECTS were handled either by declaring explicit cursors or using cursor variables. Also, the cursor variables used could be instances of strong or weak REF CURSORs. With native dynamic SQL, you don't need to use a cursor variable with hard-coded queries. Prior to Oracle8i, cursor variables were restricted in that they could only be opened for static queries. For example, the following declaration was allowed:

Add a Note Here
DECLARE
    TYPE dynamic_rc IS REF CURSOR;
    v_rc dynamic_rc;
BEGIN
    OPEN v_rc FOR SELECT order_id FROM order_items_for_region1;
    ...
    OPEN v_rc FOR SELECT order_id FROM order_items_for_region2;
    ...
END;

Add a Note HereThis way of using OPEN has the disadvantage of opening the cursor variable multiple times for each region involved if all the regions have to be processed. As you can see from the previous example, this involves writing and maintaining a lot of code. With native dynamic SQL, the cursor variable has to be opened once only. Here's what a portion of this code looks like:

Add a Note Here
DECLARE
    TYPE dynamic_rc IS REF CURSOR;
    v_rc dynamic_rc;
    Dyn_table_name VARCHAR2(100);
    Dyn_query_string VARCHAR2(1000);
BEGIN
    Dyn_table_name := 'ORDER_ITEMS_FOR_'||i_region_name;
    Dyn_query_string := 'SELECT order_id FROM '||dyn_table_name;
    OPEN v_rc FOR dyn_query_string;

    ...
END;

Add a Note HereHere, i_region_name is assumed to be a variable that's assigned dynamically with a region name.

Add a Note HereThe way to handle multirow queries with native dynamic SQL is to use a weak REF CURSOR. A weak REF CURSOR points to a work area in the System Global Area (SGA) for holding a resultset. A weak type REF CURSOR is declared and OPENed for the dynamic query string. The normal FETCHing in a LOOP and the CLOSE operation follow the opening of the cursor. Here are the steps:

  1. Add a Note HereDeclare a weak REF CURSOR type.

  2. Add a Note HereDeclare a cursor variable of this type.

  3. Add a Note HereConstruct the dynamic query string.

  4. Add a Note HereOpen the cursor variable for this dynamic query string. Any values for bind variables specified in the USING clause are evaluated in this step.

  5. Add a Note HereFETCH in a loop the rows one by one and process them. Fetching into records of type %ROWTYPE or user-defined records is allowed.

  6. Add a Note HereClose the cursor variable.

Add a Note HereThe statements

Add a Note Here
OPEN  FOR ;
OPEN  FOR 
USING ;
FETCH  INTO ;
CLOSE ;

Add a Note Hereare used to execute dynamic multirow SELECTS.

Add a Note HereAs an example, consider the procedure update_dyn_table created earlier, which accepts a particular region_name and order_id. You can extend this procedure to be processed for all orders and in a particular region. Here's the code:

Add a Note Here
Create or replace Procedure update_dyn_for_all_orders
                           (i_region_name VARCHAR2,
                            retcd OUT NUMBER,
                            errmsg OUT VARCHAR2)
Is
  Dyn_table_name VARCHAR2(100);
  Dyn_query_string VARCHAR2(1000);
  Type csr_dyn IS REF CURSOR;
   csr_dyn1 csr_dyn;
  v_order_id NUMBER;
BEGIN
  Dyn_table_name := 'ORDER_ITEMS_FOR_'||replace(trim(i_region_name),' ','_');
  Dyn_query_string := 'SELECT order_id FROM '||dyn_table_name;
  OPEN csr_dyn1 FOR dyn_query_string;
  LOOP
    FETCH csr_dyn1 INTO v_order_id;
    EXIT WHEN csr_dyn1%NOTFOUND;
    update_dyn_table(i_region_name, v_order_id, retcd, errmsg);
    IF retcd <> 0 THEN
      EXIT;
    END IF;
  END LOOP;
EXCEPTION WHEN OTHERS THEN
  Retcd := SQLCODE;
  Errmsg := SQLERRM;
END;
/

Add a Note HereThis procedure does the following:

  • Add a Note HereDeclares a type definition of type REF CURSOR named csr_dyn and a cursor variable named csr_dyn1 as an instance of the csr_dyn type. This section of the code is for processing a multirow query and is later opened for a query.

  • Add a Note HereCreates a dynamic table name by concatenating the input region name with the constant ORDER_ITEMS_FOR_ and places it in a PL/SQL variable named dyn_table_name. This process is for the order items table for each region.

  • Add a Note HereCreates a dynamically generated SELECT statement using the dyn_table_name variable and places it in a PL/SQL variable named dyn_query_string. This process is for selecting all order_id column values from the ORDER_ITEMS_FOR_ table. This SELECT returns multiple rows from each ORDER_ITEMS_FOR_ table.

  • Add a Note HereExecutes this dynamic string using the OPEN-FOR and FETCH … INTO statements. First the REF CURSOR variable is opened for the dynamic SELECT. Next, the cursor variable is fetched into a local variable named v_order_id. Then, for each row returned by the query, update_dyn_table is executed, passing the input region name and the fetched order_id as inputs. This procedure retrieves the total quantity and total price for each order_id from the ORDER_ITEMS_FOR_ table and updates the corresponding record in the ORDERS_FOR_ table.

  • Add a Note HereOutputs a return code of 0 on success in the form. of an OUT parameter named retcd. On error it outputs the corresponding SQLCODE and SQLERRM in the form. of two OUT parameters, retcd and errmsg.

Add a Note HereYou can further extend this procedure to encompass all orders in all regions. I call this procedure update_dyn_global. The logic for this procedure doesn't require dynamic SQL. For all regions in the region_tab, it loops through to execute update_dyn_for_all_orders listed previously with the region_name pointing to the current cursor row as the first input parameter. Here's the code:

Add a Note Here
Create or replace Procedure update_dyn_global
                          (retcd OUT NUMBER,
                           errmsg OUT VARCHAR2)
Is
  CURSOR csr_region IS
     SELECT region_name FROM region_tab;
BEGIN
   FOR idx IN csr_region LOOP
    update_dyn_for_all_orders(idx.region_name, retcd, errmsg);
    IF retcd <> 0 THEN
      EXIT;
    END IF;
  END LOOP;
EXCEPTION WHEN OTHERS THEN
  Retcd := SQLCODE;
  Errmsg := SQLERRM;
END;
/

Add a Note HereThis procedure does the following:

  • Add a Note HereDeclares a cursor for all the region names in the region_tab table.

  • Add a Note HereFor each row in the cursor, it invokes the update_dyn_for_all_orders procedure, passing the region name as an IN parameter to both.

  • Add a Note HereOutputs a return code of 0 on success in the form. of an OUT parameter named retcd. On error it outputs the corresponding SQLCODE and SQLERRM in the form. of two OUT parameters, retcd and errmsg.

Tip 

Add a Note HereOnly weak REF CURSORS are allowed for processing multirow queries using native dynamic SQL. If you use strong REF CURSORS, the following error is obtained: PLS-00455: cursor ‘’ cannot be used in a dynamic SQL OPEN statement.

Tip 

Add a Note HereWhen you use bind variables in the dynamic query string, the corresponding USING clause can contain only bind arguments of the IN type.

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/15225049/viewspace-628434/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2009-09-30

  • 博文量
    115
  • 访问量
    305727