Oracle存過裏面使用自定義type實現類似於事物執行中產生一張臨時表過渡數據

記錄這篇文章前,是今天寫了一個存過的需求,嗯,咋說,不想新建臨時表,所以想了下用自定義type的形式想辦法在純過裏面搞一個table級別的數據集合,先上代碼:

CREATE OR REPLACE PROCEDURE GET_TAG_COM(
INDATA IN VARCHAR2,
OUTDATA OUT SYS_REFCURSOR
)
IS


-----------------重點:自定義類型-------------------------------------------------------------
V_MYTAB1                    MYTABLE_COL1_TAB;
V_MYTAB4                    MYTABLE_COL4_TAB;
V_INDATA                    MYARRAY;
V_ONEDATA                   MYARRAY;
---------------------------------------------------------------------------------------------

V_CONDITION                 VARCHAR2(30000);
I                           NUMBER;
V_QUERYID                   NUMBER;
V_TIME                      NUMBER;
V_SQL1                      VARCHAR2(30000);
V_SQL2                      VARCHAR2(30000);



  BEGIN
       
       --自定義方法用於數據庫切分字符串------------------------------------------
       --前端入參格式:["UPDATETIME=5,isCalCount=Y,CHANNELID=21,QUERYID=1,PageIndex=1,startLine=1,recCount=20,PageSize=20"]
       --自定義函數切分後得到的格式:["UPDATETIME=5","isCalCount=Y","CHANNELID=21","QUERYID=1","PageIndex=1","startLine=1","recCount=20","PageSize=20"]
       V_INDATA             := COMMON_FUNC.SplitString(INDATA,',');

       --用於where後面的sql拼接,一個很好的解決是否要and的方法
       V_CONDITION          := ' AND 1=1';

       --循環遍歷的初始賦值
       I                    := 0
          
       FOR I IN 1 .. V_INDATA.COUNT LOOP
            --再次調用自定義切分函數,由於第一次已經把字符串切分成一個數組,這裏循環數組取出單個進行而且切分
            --切分前格式:["UPDATETIME=5"]
            --切分後格式:["UPDATETIME","5"]
           V_ONEDATA   := COMMON_FUNC.SplitString(V_INDATA(I),'=');
           
           IF     UPPER(V_ONEDATA(1))      = 'QUERYID'
           THEN

           --由於二次切分,就可以更具數組判定入參值了,甚至還能調整想要的參數
                 V_QUERYID                := TO_NUMBER(V_ONEDATA(2));
              
           ELSIF UPPER(V_ONEDATA(1))      = 'UPDATETIME'
           THEN
                 V_TIME                   := TO_NUMBER(V_ONEDATA(2)/(24*60));
           END IF;
       END LOOP;
        
        
       ---------------------------------全量--------------------------------------------------
       --根據入參可以分邏輯處理事件 
       IF V_QUERYID = 1
          THEN
            
              
          
       --------------------------------獲取參數數據--------------------------------------------
              FOR I IN 1 .. V_INDATA.COUNT LOOP
                  V_ONEDATA  := COMMON_FUNC.SplitString(V_INDATA(1),'=');
                  --這裏再次調用自定義二次切分函數,通過憑藉到where 1=1 可以得到where條件的拼接
                  SELECT V_CONDITION || DECODE(V_ONEDATA(1),
                                             'CHANNELID',
                                             ' AND TZC.Channelid IN( ''' || REPLACE(V_ONEDATA(2),'.',''',''') || ''')',
                                             'UPDATETIME',
                                             '',
                                             '') INTO  V_CONDITION FROM DUAL;
              END LOOP;
              
              
              ---------------------------------獲取商品列表----------------------------------------
              --sql拼接
              V_SQL1 := '
              
              SELECT MYTABLE_COL1_OBJ(TAG_ID) FROM (
                                                  SELECT         TCT.TAG_ID 
                                                  FROM           T_ZS_CHANNEL_BRANDORCOMMODITY TZC 
                                                  JOIN           T_COMMODITY_INFO TCI 
                                                  ON             TZC.OBJID = TCI.COMMODITY_ID AND TZC.TYPE = 2 AND TCI.STATUS = 1
                                                  JOIN           T_COMMODITY_TAG_BINDING TCTB
                                                  ON             TCTB.COMMODITY_ID = TCI.COMMODITY_ID
                                                  LEFT JOIN      T_COMMODITY_TAG TCT
                                                  ON             TCTB.TAG_ID = TCT.TAG_ID AND TCT.TAG_TYPE = 3
                                                  WHERE          TCI.ISRELEASE = 1 ' ||  V_CONDITION || '
                                                  )';
             --自定義類型定義的表,不能和拼接sql一起執行,只能單獨into,如果參考網上的select into教程,會直接報錯
             EXECUTE IMMEDIATE V_SQL1  BULK COLLECT INTO V_MYTAB1;                       
                       
              ------------------------------獲取該標籤下的所有商品及其省份--------------------------
              
--下面的join關聯了上面sql執行的結果數據,而 V_MYTAB1 就相當於一張臨時表來緩存數據,並在接下來能夠繼續使用         
              SELECT MYTABLE_COL4_OBJ(TAG_ID,TAG_NAME,PROVINCECODE,COUNTNUM)BULK COLLECT INTO V_MYTAB4 FROM (
                                            SELECT TCT.TAG_ID,TCT.TAG_NAME,TCE.PROVINCECODE,COUNT(TCI.COMMODITY_ID) AS COUNTNUM
                                             FROM             T_COMMODITY_TAG TCT 
                                             JOIN             T_COMMODITY_TAG_BINDING TCTB
                                             ON               TCTB.TAG_ID = TCT.TAG_ID AND TCT.TAG_TYPE = 3
                                             JOIN             T_COMMODITY_INFO TCI
                                             ON               TCTB.COMMODITY_ID = TCI.COMMODITY_ID AND TCI.STATUS = 1 AND TCI.ISRELEASE = 1
                                             JOIN             T_COMMODITY_EXCHANGESCOPE TCE
                                             ON               TCE.COMMODITYID = TCI.COMMODITY_ID
                                             JOIN             TABLE(V_MYTAB1) TAB ON TCT.TAG_ID = TAB.COL1
                                             GROUP BY TCT.TAG_ID,TCT.TAG_NAME,TCE.PROVINCECODE
                                             );
            --遊標的out不能直接返回自定義類型的表,只能通過查詢的方式返回自定義類型的表數據           
            OPEN OUTDATA FOR SELECT * FROM  TABLE(V_MYTAB4);

          
          --後面的邏輯都差不多
          -----------------------------------------獲取增量數據---------------------------------------
          
          ELSIF  V_QUERYID = 2
            THEN
                 
                FOR I IN 1 .. V_INDATA.COUNT LOOP
                    V_ONEDATA  := COMMON_FUNC.SplitString(V_INDATA(1),'=');
                    SELECT V_CONDITION || DECODE(V_ONEDATA(1),
                                             'CHANNELID',
                                             ' AND TZC.Channelid IN( ''' || REPLACE(V_ONEDATA(2),'.',''',''') || ''')',
                                             ''
                
                                              ) INTO V_CONDITION FROM DUAL;
                END LOOP;
                
                ------------------------------------獲取變動過的tagid---------------------------------                
                V_SQL1 := '
                
                SELECT MYTABLE_COL1_OBJ(TAG_ID) FROM (
                                                             SELECT  TCT.TAG_ID 
                                                             FROM           T_ZS_CHANNEL_BRANDORCOMMODITY TZC 
                                                             JOIN           T_COMMODITY_INFO TCI 
                                                             ON             TZC.OBJID = TCI.COMMODITY_ID AND TZC.TYPE = 2 AND TCI.STATUS = 1
                                                             LEFT JOIN           T_COMMODITY_TAG_BINDING TCTB
                                                             ON             TCTB.COMMODITY_ID = TCI.COMMODITY_ID
                                                             LEFT JOIN      T_COMMODITY_TAG TCT
                                                             ON             TCTB.TAG_ID = TCT.TAG_ID AND TCT.TAG_TYPE = 3
                                                             WHERE          TCI.ISRELEASE = 1 
                                                             AND            TCT.UPDATE_TIME  > SYSDATE-'|| V_TIME || '
                                                             OR             TCI.UPDATE_TIME  > SYSDATE-'|| V_TIME || '
                                                             OR             TCTB.UPDATE_TIME > SYSDATE-'|| V_TIME || V_CONDITION || '
                                                             )';
               EXECUTE IMMEDIATE V_SQL1 BULK COLLECT INTO V_MYTAB1;             
  

          
          
              ------------------------------獲取該標籤下的所有商品及其省份--------------------------
              
              SELECT MYTABLE_COL4_OBJ(TAG_ID,TAG_NAME,PROVINCECODE,COUNTNUM)BULK COLLECT INTO V_MYTAB4 FROM (
              SELECT TCT.TAG_ID,TCT.TAG_NAME,TCE.PROVINCECODE,COUNT(TCI.COMMODITY_ID) AS COUNTNUM 
                                   FROM             T_COMMODITY_TAG TCT 
                                   JOIN             T_COMMODITY_TAG_BINDING TCTB
                                   ON               TCTB.TAG_ID = TCT.TAG_ID AND TCT.TAG_TYPE = 3
                                   JOIN             T_COMMODITY_INFO TCI
                                   ON               TCTB.COMMODITY_ID = TCI.COMMODITY_ID AND TCI.STATUS = 1 AND TCI.ISRELEASE = 1
                                   JOIN             T_COMMODITY_EXCHANGESCOPE TCE
                                   ON               TCE.COMMODITYID = TCI.COMMODITY_ID
                                   JOIN             TABLE(V_MYTAB1) TAB ON TCT.TAG_ID = TAB.COL1
                                   GROUP BY TCT.TAG_ID,TCT.TAG_NAME,TCE.PROVINCECODE
                                   );
           
            OPEN OUTDATA FOR SELECT * FROM  TABLE(V_MYTAB4);
      END IF;  
                 

  END GET_TAG_COM;

 

 

這個存過精彩點在哪裏?

 

V_MYTAB1                    MYTABLE_COL1_TAB;
V_MYTAB4                    MYTABLE_COL4_TAB;
V_INDATA                    MYARRAY;
V_ONEDATA                   MYARRAY;

這裏的自定義類型解決了我想要在sql執行過程中產生的中間數據需要緩存,並在接下來的sql中關聯這些數據進行使用;

要想玩懂這種方法,需要弄清楚Oracle的type,

什麼是type?

type可以在哪些位置使用?


在這個分割線裏,插入一句,存過產生的臨時數據還有另一種解決辦法,就是建立一張僅存儲過程能有調用的臨時表,這種臨時表有着事物執行完後自動刪除數據,不允許用戶訪問等權限,也能保障和解決存過需要使用過度數據的方法,具體方式這裏不做記錄;


繼續type

Oracle有5中type類型,而且在ocp考試的時候會考這點,type的解釋官方文檔地址:

https://docs.oracle.com/cd/E18283_01/appdev.112/e11822/adobjcol.htm

 

再Oracle中,作爲一個DBA,我之前問過我自己一個問題:

爲什麼create table a();這個創建表a的語句要用create table?爲什麼不直接是create a?

爲什麼定義字段a,後面必須要跟個類型,不管是int還是varchar2;

之後學習過程中,我瞭解到,數據存儲結構可以以類型來作爲區分。

table是一種類型,varchar2也是一種類型,同樣,官方定義的類型再多,也有需要定製的時候。所以提供一種獨特的類型:

自定義type;

既然type可以定義數據結構,那麼同樣的,我想要自定義數據結構的時候,就需要使用自定義type這種類型方式;


在PLSQL中

左邊欄,在存過後邊緊跟着的就是type文件夾,定義的type都放在裏面

type和package差不多,一樣有頭,一樣有body;

結構雖然差不多,但是type可以只要頭就能夠使用,不像package必須要頭和body一起才能使用;

CREATE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20),
  MAP MEMBER FUNCTION get_idno RETURN NUMBER, 
  MEMBER PROCEDURE display_details ( SELF IN OUT NOCOPY person_typ ) );
/

CREATE TYPE BODY person_typ AS
  MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
  BEGIN
    RETURN idno;
  END;
  MEMBER PROCEDURE display_details ( SELF IN OUT NOCOPY person_typ ) IS
  BEGIN
    -- use the put_line procedure of the DBMS_OUTPUT package to display details
    DBMS_OUTPUT.put_line(TO_CHAR(idno) || ' - '  || name || ' - '  || phone);
  END;
END;
/

CREATE TYPE people_typ AS TABLE OF person_typ; -- nested table type
/

以上是官方創建type的舉例,裏面的body是爲了返回個頭中map部分而創建的,如果用不上可以不寫,比如寫成:

CREATE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20),
   );


CREATE TYPE people_typ AS TABLE OF person_typ; -- nested table type

官方這裏舉例定義了一個三個字段的object;

所以後面必須要跟一個crate type 語句as 一下這個object才能構成一個type;

如果只需要一個字段的一維數組,直接用create type就可以創建了,比如我這裏用的存儲數據type定義

CREATE OR REPLACE TYPE "MYARRAY"     is Table of varchar2(4000)

這裏可能就有點繞了,爲什麼要object,

其實object是固定的寫法,自定義type固定創建步驟是:

1、創建一個需求格式的object

2、創建type並且應用所創建的object


之所以創建myarray直接用了varchar2,而沒有指向一個object,是因爲varchar2本身就是一個單列的object,
所以創建單列自定義type的時候,
不需要額外的定義object,直接指向Oracle擁有的object即可;同樣的,number,time等也是單列object,
所以創建myarray時沒有單獨定義一個obj,由於oracle沒有自帶多列的obj,所以創建多列自定義type時需要定義一個多列obj;

 

說了這麼多爲什麼要自定義type,一般來說自定義type都是在存過裏面用的,因爲存過在處理數據庫邏輯的時候可能產生很多中間數據,或者會收到外部的不是數據庫結構的數據,當我們需要解析這些數據的時候,需要自己處理,而處理的第一步就是把數據緩存下來,定義type的作用就是爲了給緩存數據一個緩存空間。

這樣就能在存過裏面使用我們定義的這個type,就有了一個空間去榮譽數據;

比如本次所用的四個字段type:

先創建一個object

 create or replace type MYTABLE_COL4_OBJ as object
 (
   col1 varchar2(100),
   col2 varchar2(100),
   col3 varchar2(100),
   col4 varchar2(100)
 )

在創建一個type 使用object類型:

create or replace type MYTABLE_COL4_TAB as table of MYTABLE_COL4_OBJ;

這樣我就能在存過中定義一個變量,讓這個變量成爲一張四個字段的表:

同樣MYARRAY也是自定一個字段的表;

當然創建自定義type有table/array等多種類型,需要使用哪種或者這兩種的區別可以在官方文檔中查找;

 

注意幾點:

1、採用自定義type的變量當作表用,不能直接返回給遊標,解決辦法是返回他的select查詢結果

2、同樣的這種變量不能和帶有其他變量的拼接sql一起執行;解決辦法就是執行拼接sql後把值給再into到這個變量中;

3、join等使用自定義type表時,table類型需要加上table()才能正常使用,直接使用是會報錯的;

4、官方的語句的MAP MEMBER FUNCTION get_idno RETURN NUMBER這句話,其實很重要,如果用不上可以不加,

但是他能做到一些非常棒的功能,比如你傳入出生年齡能夠返回傳入的出生年齡和歲數,具體歲數的實現就是用map跳轉到body裏面去實現的,這裏沒有用到body,就不做過多記錄,主要是body聊下來就太長了。

 

當然本次用到的自定義切割方法,不在此討論;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章