顯示具有 PL-SQL 標籤的文章。 顯示所有文章
顯示具有 PL-SQL 標籤的文章。 顯示所有文章

2023-07-06

[Oracle]樞紐分析的好方法 PIVOT

 有時候計算庫齡與帳齡時,當計算完後總是要做個樞紐分析,才能得到最終的結果,忽然是否想到PLSQL是否有提供這個的方法可以使用




CREATE TABLE TEST_PIVOT(
 CUST_NUMBER     VARCHAR2(10),
 NAME            VARCHAR2(10),
 AR_AMOUNT       NUMBER,
 DAYS_PAST_DUE   NUMBER
)



INSERT INTO TEST_PIVOT(CUST_NUMBER,NAME,AR_AMOUNT,DAYS_PAST_DUE)VALUES('A01','Judy',1000,5);
INSERT INTO TEST_PIVOT(CUST_NUMBER,NAME,AR_AMOUNT,DAYS_PAST_DUE)VALUES('A01','Candy',500,20);
INSERT INTO TEST_PIVOT(CUST_NUMBER,NAME,AR_AMOUNT,DAYS_PAST_DUE)VALUES('A02','Teddy',999,10);
INSERT INTO TEST_PIVOT(CUST_NUMBER,NAME,AR_AMOUNT,DAYS_PAST_DUE)VALUES('A03','Andy',651,30);
INSERT INTO TEST_PIVOT(CUST_NUMBER,NAME,AR_AMOUNT,DAYS_PAST_DUE)VALUES('A04','Ray',300,-1);

SELECT * FROM TEST_PIVOT;

SELECT CUST_NUMBER, 
       NAME, 
       AR_AMOUNT,
       DAYS_PAST_DUE, 
       CASE
         WHEN DAYS_PAST_DUE < 0 THEN '未逾期'
         WHEN DAYS_PAST_DUE BETWEEN  1 AND 10  THEN '1~10天'
         WHEN DAYS_PAST_DUE BETWEEN 11 AND 20 THEN '11~20天'
         WHEN DAYS_PAST_DUE BETWEEN 21 AND 31 THEN '21~31天'
         WHEN DAYS_PAST_DUE > 32 THEN '大於32天'
       END AREA
  FROM TEST_PIVOT

CUST_NUMBER NAME      AR_AMOUNT   DAYS_PAST_DUE   AREA
-----------------------------------------------------------------
A01      Judy        1000         5               1~10天
A01      Candy        500         20              11~20天
A02      Teddy        999         10              1~10天
A03      Andy         651         30              21~31天
A04      Ray          300         -1              未逾期



SELECT CUST_NUMBER,
       NAME,
       AR_AMOUNT_SUM,
       "'未逾期'",
       "'1~10天'",
       "'11~20天'",
       "'21~31天'",
       "'大於32天'"
  FROM (SELECT PRE_PV.CUST_NUMBER,
               PRE_PV.NAME,
               PRE_PV.AREA,
               PRE_PV.AR_AMOUNT,
               SUM(PRE_PV.AR_AMOUNT) OVER(PARTITION BY CUST_NUMBER, NAME) AR_AMOUNT_SUM
          FROM (SELECT CUST_NUMBER,
                       NAME,
                       AR_AMOUNT,
                       CASE
                         WHEN DAYS_PAST_DUE < 0 THEN '未逾期'
                         WHEN DAYS_PAST_DUE BETWEEN 1 AND 10 THEN '1~10天'
                         WHEN DAYS_PAST_DUE BETWEEN 11 AND 20 THEN '11~20天'
                         WHEN DAYS_PAST_DUE BETWEEN 21 AND 31 THEN '21~31天'
                         WHEN DAYS_PAST_DUE > 32 THEN '大於32天'
                       END AREA
                  FROM TEST_PIVOT) PRE_PV) PIVOT(SUM(AR_AMOUNT) FOR AREA IN('未逾期', '1~10天', '11~20天', '21~31天', '大於32天'))
                  
CUST_NUMBER	NAME	AR_AMOUNT_SUM	'未逾期'	'1~10天'	'11~20天'	'21~31天'	'大於32天'
A01	        Judy	1000		                   1000			
A03	        Andy	651				                                       651	
A02	        Teddy	999		                      999			
A04	        Ray	  300	              300				
A01	        Candy	500			                            500		

[Oracle]多行數據合併,並分號分隔,LISTAGG

 最近常常遇到要寄送告警資料給同仁,常常需要整理一些郵件,分享一個好方法



CREATE TABLE TEST_LISTAGG(
 DEPID      VARCHAR2(10),
 USER_NAME  VARCHAR2(10),
 MAIL       VARCHAR2(50)
)



INSERT INTO TEST_LISTAGG(DEPID,USER_NAME,MAIL)VALUES('A01','Judy','Judy@1234');
INSERT INTO TEST_LISTAGG(DEPID,USER_NAME,MAIL)VALUES('A01','Candy','Candy@1234');
INSERT INTO TEST_LISTAGG(DEPID,USER_NAME,MAIL)VALUES('A02','Teddy','Teddy@1234');
INSERT INTO TEST_LISTAGG(DEPID,USER_NAME,MAIL)VALUES('A03','Andy','Andy@1234');

SELECT * FROM TEST_LISTAGG;

A01	Judy	Judy@1234
A01	Candy	Candy@1234
A02	Teddy	Teddy@1234
A03	Andy	Andy@1234


SELECT DEPID, 
       LISTAGG(MAIL,';') WITHIN GROUP(ORDER BY DEPID) AS NEW_MAIL
  FROM TEST_LISTAGG
 GROUP BY DEPID;
 

A01	Candy@1234;Judy@1234
A02	Teddy@1234
A03	Andy@1234

2023-02-15

[Oracle]讀取Clob欄位資料

方法一

SELECT DBMS_LOB.SUBSTR(欄位, 讀取長度,第幾個開始)

SELECT DBMS_LOB.SUBSTR(A.ITEM133, 2000)

方法二

這就比較複雜一點,因為背後其實轉成varchar2,所以會有長度限制的問題



DECLARE
  V_CLOB       CLOB;
  V_CLOB_LEN   NUMBER;
  V_OFFSET     NUMBER := 1;
  V_CHUNK_SIZE NUMBER := 32767;
  V_CLOB_CHUNK VARCHAR2(32767);

  CURSOR C_CLOB IS
    SELECT --HP.PARTY_NAME,
    --HCA.ACCOUNT_NUMBER,
    --D.DESCRIPTION,
    --AD.PK1_VALUE,
     DLT.LONG_TEXT
    --INTO CLOB_COLUMN
      FROM FND_DOCUMENTS_VL           D,
           FND_ATTACHED_DOCUMENTS     AD,
           FND_DOCUMENT_ENTITIES      E,
           FND_USER                   U,
           FND_DOCUMENT_CATEGORIES_TL CL,
           FND_DM_NODES               NODE,
           FND_DOCUMENTS_LONG_TEXT    DLT,
           HZ_PARTIES                 HP,
           HZ_CUST_ACCOUNTS_ALL       HCA
     WHERE AD.DOCUMENT_ID = D.DOCUMENT_ID
       AND AD.ENTITY_NAME = E.DATA_OBJECT_CODE(+)
       AND AD.LAST_UPDATED_BY = U.USER_ID(+)
       AND CL.LANGUAGE = USERENV('LANG')
       AND AD.ENTITY_NAME = 'AR_CUSTOMERS'
       AND HP.PARTY_ID = HCA.PARTY_ID
       AND HCA.CUST_ACCOUNT_ID = PK1_VALUE
       AND PK1_VALUE = '492970' --CUST_ACCOUNT_ID
       AND CL.CATEGORY_ID = NVL(AD.CATEGORY_ID, D.CATEGORY_ID)
       AND D.DM_NODE = NODE.NODE_ID(+)
       AND D.MEDIA_ID = DLT.MEDIA_ID
       AND EXISTS (SELECT 1
              FROM HZ_CUST_ACCT_SITES_ALL AA
             WHERE AA.CUST_ACCOUNT_ID = HCA.CUST_ACCOUNT_ID
               AND AA.ORG_ID = 136);
BEGIN
  FOR R_CLOB IN C_CLOB LOOP
    V_CLOB     := R_CLOB.LONG_TEXT;
    V_CLOB_LEN := DBMS_LOB.GETLENGTH(V_CLOB);
  
    WHILE V_OFFSET <= V_CLOB_LEN LOOP
      DBMS_LOB.READ(V_CLOB, V_CHUNK_SIZE, V_OFFSET, V_CLOB_CHUNK);
      DBMS_OUTPUT.PUT_LINE(V_CLOB_CHUNK);
      V_OFFSET := V_OFFSET + V_CHUNK_SIZE;
    END LOOP;
    V_OFFSET := 1;
  END LOOP;
END;

2023-01-31

[Oracle]Array 練習

參考網站

http://abu.tw/2010/04/plsql-table-oracle-array-like.html

DECLARE
  -- 宣告 RECORD, TYPE 及變數   
  TYPE R_HANDSET IS RECORD(
    BRAND      VARCHAR2(10),
    MODEL_NAME VARCHAR2(20),
    PRICE      NUMBER);
  TYPE T_HANDSET IS TABLE OF R_HANDSET INDEX BY PLS_INTEGER;

  HANDSETS T_HANDSET;
BEGIN
  -- 塞值進 RECORD ARRAY  
  HANDSETS(1).BRAND := 'HTC';
  HANDSETS(1).MODEL_NAME := 'TATTOO';
  HANDSETS(1).PRICE := 6000;
  HANDSETS(2).BRAND := 'APPLE';
  HANDSETS(2).MODEL_NAME := 'IPHONE';
  HANDSETS(2).PRICE := 27000;
  HANDSETS(3).BRAND := 'NOKIA';
  HANDSETS(3).MODEL_NAME := 'N82';
  HANDSETS(3).PRICE := 15000;

  FOR I IN 1 .. HANDSETS.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE('第 ' || TO_CHAR(I) || ' 筆 - ');
    DBMS_OUTPUT.PUT_LINE('廠牌 : ' || HANDSETS(I).BRAND);
    DBMS_OUTPUT.PUT_LINE('名稱 : ' || HANDSETS(I).MODEL_NAME);
    DBMS_OUTPUT.PUT_LINE('價格 : ' || TO_CHAR(HANDSETS(I).PRICE));
    DBMS_OUTPUT.PUT_LINE(' ');
  END LOOP;
END;

2023-01-11

[Oracle]郵件測試

搭配以下程式
  • BLOB_TO_CLOB
  • CLOB_TO_BLOB
  • SEND_MAIL
DECLARE
  P_SUBJECT  VARCHAR2(200) := '[Oracle]測試報表'; --信件標題
  P_RCPT     VARCHAR2(200) := 'yolin_chen@XXXX.com.tw'; --,分隔郵件
  P_CC       VARCHAR2(200) := '';
  P_MSG      VARCHAR2(4000) := '';
  P_MSG_CLOB CLOB;
  P_FILENAME VARCHAR2(50) := '測試檔案';
  P_HTML     VARCHAR2(2) := 'Y';

  V_CRLF VARCHAR2(2) := CHR(13) || CHR(10);

  CURSOR CUR IS
    SELECT * FROM FND_USER WHERE 1=1;

BEGIN
  FOR C1 IN CUR LOOP
    P_MSG_CLOB := P_MSG_CLOB || TO_CHAR(C1.USER_ID) || ',' ||
                  TO_CHAR(C1.USER_NAME)
                 --|| ','
                 --|| TO_CHAR(X.CREATION_DATE, 'YYYY-MM-DD HH24:MI:SS')
                  || V_CRLF;
  END LOOP;

  --這邊已將UTF-8改為UTF-8 BOM,Windows系統開啟才不會亂碼
  SELECT ACE_UTL_TOOLS.BLOB_TO_CLOB(ACE_UTL_TOOLS.CLOB_TO_BLOB(P_MSG_CLOB))
    INTO P_MSG_CLOB
    FROM DUAL;

  P_MSG := '';
  P_MSG :=  --這邊可定義CSS
   P_MSG ||
           ' ';
  P_MSG :=  --這邊可定義郵件內文
   P_MSG || '


' || '

此信由系統發出請勿回覆, 如有問題請洽IT

' || '

PROCEDURE: ACEOMR004

'; P_MSG := --這邊可定義表格 P_MSG || '' || ' ' || ' ' || ' ' || ' ' || ' ' || ' ' || ' ' || ' ' || '
欄位一欄位二
你好HELLO
'; P_MSG := P_MSG || ' '; ACE_UTL_TOOLS.SEND_MAIL(P_SUBJECT, P_RCPT, P_CC, P_MSG, P_MSG_CLOB, --附件內容 P_FILENAME, --附件名稱 P_HTML); END;

[Oracle]BLOB_TO_CLOB

  FUNCTION BLOB_TO_CLOB(P_DATA IN BLOB) RETURN CLOB AS
    L_CLOB         CLOB;
    L_DEST_OFFSET  PLS_INTEGER := 1;
    L_SRC_OFFSET   PLS_INTEGER := 1;
    L_LANG_CONTEXT PLS_INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    L_WARNING      PLS_INTEGER;
  BEGIN
  
    DBMS_LOB.CREATETEMPORARY(LOB_LOC => L_CLOB, CACHE => TRUE);
  
    DBMS_LOB.CONVERTTOCLOB(DEST_LOB     => L_CLOB,
                           SRC_BLOB     => P_DATA,
                           AMOUNT       => DBMS_LOB.LOBMAXSIZE,
                           DEST_OFFSET  => L_DEST_OFFSET,
                           SRC_OFFSET   => L_SRC_OFFSET,
                           BLOB_CSID    => DBMS_LOB.DEFAULT_CSID,
                           LANG_CONTEXT => L_LANG_CONTEXT,
                           WARNING      => L_WARNING);
  
    RETURN L_CLOB;
  END;

[Oracle]CLOB_TO_BLOB

  
  FUNCTION CLOB_TO_BLOB(P_DATA IN CLOB) RETURN BLOB AS
    L_BLOB         BLOB;
    L_DEST_OFFSET  PLS_INTEGER := 1;
    L_SRC_OFFSET   PLS_INTEGER := 1;
    L_LANG_CONTEXT PLS_INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    L_WARNING      PLS_INTEGER := DBMS_LOB.WARN_INCONVERTIBLE_CHAR;
    B              BLOB;
    C              BLOB;
  BEGIN
  
    DBMS_LOB.CREATETEMPORARY(LOB_LOC => L_BLOB, CACHE => TRUE);
  
    DBMS_LOB.CONVERTTOBLOB(DEST_LOB     => L_BLOB,
                           SRC_CLOB     => P_DATA,
                           AMOUNT       => DBMS_LOB.LOBMAXSIZE,
                           DEST_OFFSET  => L_DEST_OFFSET,
                           SRC_OFFSET   => L_SRC_OFFSET,
                           BLOB_CSID    => DBMS_LOB.DEFAULT_CSID,
                           LANG_CONTEXT => L_LANG_CONTEXT,
                           WARNING      => L_WARNING);
  
    --RETURN L_BLOB;
    BEGIN
      SELECT 'EFBBBF' INTO B FROM DUAL;
      DBMS_LOB.CREATETEMPORARY(C, TRUE);
      DBMS_LOB.APPEND(C, B);
      DBMS_LOB.APPEND(C, L_BLOB);
    END;
    RETURN C;
  END;

2022-12-28

[Oracle]發送郵件,並突破VARCHAR2字元上限

需要搭配BLOB to CLOB轉換
  PROCEDURE SEND_MAIL(P_SUBJECT  VARCHAR2, --信件標題
                      P_RCPT     VARCHAR2, --收件者 以分號區分
                      P_CC       VARCHAR2 DEFAULT NULL, --副本 以分號區分
                      P_MSG      CLOB, --郵件內容
                      P_MSG_CLOB CLOB, --附件內容
                      P_FILENAME VARCHAR2, --附件名稱
                      P_HTML     VARCHAR2 DEFAULT 'Y') IS
 
    MAIL_CONN     UTL_SMTP.CONNECTION;
    V_USERNAME    VARCHAR2(200) := UTL_ENCODE.TEXT_ENCODE('mailuser@XXXX.com.tw',
                                                          'WE8ISO8859P1',
                                                          UTL_ENCODE.BASE64);
    V_PASSWORD    VARCHAR2(200) := UTL_ENCODE.TEXT_ENCODE('mail@@XXXX',
                                                          'WE8ISO8859P1',
                                                          UTL_ENCODE.BASE64);
    V_MAIL_SERVER VARCHAR2(200) := 'smtp.office365.com';
    V_MAILER      VARCHAR2(200) := 'mailuser@XXXX.com.tw';
    V_RCPT        VARCHAR2(4000) := REPLACE(REPLACE(P_RCPT, ' ', ''),' ','');
    V_CC          VARCHAR2(4000) := REPLACE(REPLACE(P_CC, ' ', ''),' ','');
    V_CRLF        VARCHAR2(2) := CHR(13) || CHR(10);
    V_IS_GROUP    NUMBER := 0;
    V_IS_GROUP_CC NUMBER := 0;
    V_RCPT_CNT    NUMBER := 0;
    V_CC_CNT      NUMBER := 0;
    V_RCPT_MAIL   VARCHAR2(2000);
    V_CC_MAIL     VARCHAR2(2000);
    V_REPLY       UTL_SMTP.REPLY;
    L_STEP        PLS_INTEGER := 12000; -- make sure you set a multiple of 3 not higher than 24573
  
  BEGIN
    --開啟 Mail Connection 物件
    MAIL_CONN := UTL_SMTP.OPEN_CONNECTION(HOST => V_MAIL_SERVER,
                                          PORT => 25,
                                          --TX_TIMEOUT                    => 100,
                                          WALLET_PATH                   => 'file:/u01/wallets/office365', --'file:/u1/PROD/wallets/office365',
                                          WALLET_PASSWORD               => 'XXXX',
                                          SECURE_CONNECTION_BEFORE_SMTP => FALSE);
    --建立連線
    UTL_SMTP.EHLO(MAIL_CONN, V_MAIL_SERVER); --DO NOT USE HELO
  
    V_REPLY := UTL_SMTP.STARTTLS(MAIL_CONN);
    UTL_SMTP.EHLO(MAIL_CONN, V_MAIL_SERVER); --DO NOT USE HELO
  
    UTL_SMTP.COMMAND(MAIL_CONN, 'AUTH LOGIN');
    UTL_SMTP.COMMAND(MAIL_CONN, V_USERNAME);
    UTL_SMTP.COMMAND(MAIL_CONN, V_PASSWORD);
  
    --設定寄件者
    UTL_SMTP.MAIL(MAIL_CONN, V_MAILER);
  
    --設定收件者
    V_IS_GROUP := INSTR(V_RCPT, ',');
  
    IF V_IS_GROUP > 0 THEN
      --多筆收件者
    
      LOOP
      
        SELECT INSTR(V_RCPT, ',') INTO V_RCPT_CNT FROM DUAL;
      
        IF V_RCPT_CNT != 0 THEN
          V_RCPT_MAIL := SUBSTR(V_RCPT, 1, V_RCPT_CNT - 1);
          UTL_SMTP.RCPT(MAIL_CONN, V_RCPT_MAIL);
        ELSE
          UTL_SMTP.RCPT(MAIL_CONN, V_RCPT);
          EXIT;
        END IF;
      
        V_RCPT := SUBSTR(V_RCPT, V_RCPT_CNT + 1);
      
      END LOOP;
    
    ELSE
      --單筆收件者
      UTL_SMTP.RCPT(MAIL_CONN, P_RCPT);
    END IF;
  
    --設定副本
    V_IS_GROUP_CC := INSTR(V_CC, ',');
  
    IF NVL(V_IS_GROUP_CC, 0) > 0 THEN
      --多筆收件者
      LOOP
        SELECT INSTR(V_CC, ',') INTO V_CC_CNT FROM DUAL;
      
        IF V_CC_CNT != 0 THEN
          V_CC_MAIL := SUBSTR(V_CC, 1, V_CC_CNT - 1);
          UTL_SMTP.RCPT(MAIL_CONN, V_CC_MAIL);
        ELSE
          UTL_SMTP.RCPT(MAIL_CONN, V_CC);
          EXIT;
        END IF;
      
        V_CC := SUBSTR(V_CC, V_CC_CNT + 1);
      
      END LOOP;
    
    ELSE
      --單筆收件者
      IF P_CC IS NOT NULL THEN
        UTL_SMTP.RCPT(MAIL_CONN, P_CC);
      END IF;
    END IF;
  
    --設定發信內容
    UTL_SMTP.OPEN_DATA(MAIL_CONN);
  
    --寫入發信標題
    --UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('DATE: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('FROM: ' || V_MAILER || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('TO: ' || P_RCPT || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CC: ' || P_CC || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('SUBJECT: ' || P_SUBJECT || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('MIME-VERSION: 1.0' || V_CRLF));
    --Add v1.02
    IF P_FILENAME IS NOT NULL THEN
      --有附件
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TYPE: MULTIPART/MIXED; BOUNDARY="SECBOUND"' || V_CRLF || V_CRLF));
    ELSE
      --無附件
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TYPE: MULTIPART/ALTERNATIVE; BOUNDARY="SECBOUND"' || V_CRLF || V_CRLF));
    END IF;
  
    --設定發信內容
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('--SECBOUND' || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TYPE: TEXT/HTML; ' || V_CRLF || V_CRLF));
    --UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TYPE: TEXT/PLAIN; ' || V_CRLF || V_CRLF));--等待擴充
  
    --UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN, UTL_RAW.CAST_TO_RAW(P_MSG));
    FOR I IN 0 .. TRUNC((DBMS_LOB.GETLENGTH(P_MSG) - 1) / L_STEP) LOOP
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(P_MSG,L_STEP,I * L_STEP + 1)));
    END LOOP;
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('--SECBOUND' || V_CRLF));
    UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('--SECBOUND' || V_CRLF));
  
    IF P_FILENAME IS NOT NULL THEN
      --設定附件
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TYPE: TEXT/CSV; NAME="' || P_FILENAME || '.CSV"' ||V_CRLF));
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-TRANSFER-ENCODING: 8BIT' || V_CRLF));
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('CONTENT-DISPOSITION: ATTACHMENT;' || V_CRLF));
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW('FILENAME="' || P_FILENAME || '.CSV"' || V_CRLF));
      UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN, UTL_RAW.CAST_TO_RAW(V_CRLF));
      FOR I IN 0 .. TRUNC((DBMS_LOB.GETLENGTH(P_MSG_CLOB) - 1) / L_STEP) LOOP
        UTL_SMTP.WRITE_RAW_DATA(MAIL_CONN,UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(P_MSG_CLOB,L_STEP,I * L_STEP + 1)));
      END LOOP;
    END IF;
  
    --關閉發信內容
    UTL_SMTP.CLOSE_DATA(MAIL_CONN);
  
    --結束連線
    UTL_SMTP.QUIT(MAIL_CONN);
    DBMS_OUTPUT.PUT_LINE('郵件發送成功');
  EXCEPTION
    WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
      DBMS_OUTPUT.PUT_LINE('郵件發送失敗');
      UTL_SMTP.QUIT(MAIL_CONN);
      DBMS_OUTPUT.PUT_LINE(SQLCODE || SQLERRM);
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE || SQLERRM);
  END;

2022-12-26

[Oracle]Iterative Processing with Loops


The Simple Loop
語法結構
Loop
EXECUTABLE_STATEMENTS
END LOOP;
THE WHILE LOOP
語法結構
WHILE CONDITION
LOOP
EXECUTABLE_STATEMENTS
END LOOP;

The Numeric FOR Loop
語法結構
FOR LOOP_COUNTER IN 1 .. 10
LOOP
EXECUTABLE_STATEMENTS
END LOOP;

FOR LOOP_COUNTER IN REVERSE 10 .. 1
LOOP
EXECUTABLE_STATEMENTS
END LOOP;

The Cursor FOR Loop
FOR RECORD IN {}

2017-05-17

[Oracle]Start with connect by prior用法

參考資料 https://dotblogs.com.tw/jeff-yeh/2009/05/20/8489 http://tomkuo139.blogspot.tw/2009/06/oracle-plsql-treeview.html https://read01.com/0zGL7o.html 最近與C大研究了Connect by,發現還是有些疑問,開始上網找答案 語法架構
SELECT COL1, COL2,.. .
  FROM TABLE [
 WHERE CONDITION.. . ]
 START WITH 要放在最上層的條件
CONNECT BY [ PRIOR ] CHILD_COLUMN = PARENT_COLUMN [
 ORDER [ SIBLINGS ] BY SORT .. . ]
若 prior 有提供,則會查詢出本階與其所有下階資料。 若 prior 無提供,則只查詢出本階資料。 若 siblings 有提供,則會按父子階層排序,再按指定欄位排序。 若 siblings 無提供,則會按指定欄位排序。 Child_Column = Parent_Column,視為 "Child 的父階是哪個"。 查詢會先按 start with 展開 Treeview,再過濾 where 條件。  
  架設測試環境 CREATE TABLE IT_CONNECT_BY ( EMP   VARCHAR2(20), NAME  VARCHAR2(20), EMAIL VARCHAR2(20), BOSS  VARCHAR2(20) )
測試資料
EMP NAME EMAIL BOSS
A0001 James james@aaa.com
A0002 Porz porz@aaa.com A0001
A0003 Sandy sandy@aaa.com A0002
A0004 Grace grace@aaa.com A0003
--沒有 PRIOR 尋找 A0003 的主管
SELECT *
  FROM IT_CONNECT_BY CB
CONNECT BY CB.EMP = CB.BOSS
 START WITH CB.EMP = 'A0003';
結果
EMP NAME EMAIL BOSS
A0003 Sandy sandy@aaa.com A0002
--有 PRIOR 尋找 A0003 的主管
--PRIOR 在左邊
SELECT *
FROM IT_CONNECT_BY CB
CONNECT BY PRIOR CB.BOSS = CB.EMP
START WITH CB.EMP = 'A0003';
結果
EMP NAME EMAIL BOSS
A0003 Sandy sandy@aaa.com A0002
A0002 Porz porz@aaa.com A0001
A0001 James james@aaa.com
--有 PRIOR 尋找 A0003 的主管
--PRIOR 在右邊
SELECT *
  FROM IT_CONNECT_BY CB
CONNECT BY CB.BOSS = PRIOR CB.EMP
 START WITH CB.EMP = 'A0003';
結果
EMP NAME EMAIL BOSS
A0003 Sandy sandy@aaa.com A0002
A0004 Grace grace@aaa.com A0003
最後發現 PRIOR 的位置,會決定是由A0003出發向上展或者向下

2017-02-23

[Oracle]Global temporary table

 可能有需求需要常常建立某些暫存性資料表,今天同事說有另外一種方式

Global temporary table,建立方式如下

CREATE GLOBAL TEMPORARY TABLE TEMP_TABLE
(
  ATTR1   VARCHAR2(200),
  ATTR2   VARCHAR2(200),
  ATTR3   VARCHAR2(200),
  ATTR4   VARCHAR2(200),
  ATTR5   VARCHAR2(200),
  ATTR6   VARCHAR2(200),
  ATTR7   VARCHAR2(200),
  ATTR8   VARCHAR2(200),
  ATTR9   VARCHAR2(200),
  ATTR10  VARCHAR2(200),
  RPT_ID  VARCHAR2(32),
  LINE_NO NUMBER
)
ON COMMIT DELETE ROWS;

當在同一個Session下是可以看到資料,但是離開Session後,資料就會被清空

2017-02-16

[Oracle] PL/SQL 系統錯誤資訊

參考網站:http://tomkuo139.blogspot.tw/2015/06/oracle-plsql-db-error-message.html

抓取錯誤訊息,今天才發現11g以後的版本,有提供更明確的錯誤訊息

EXCEPTION
  WHEN OTHERS THEN
    P_MESSAGE := SQLCODE || '   ' || SQLERRM;
EXCEPTION
  WHEN OTHERS THEN
    P_MESSAGE := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ||
                 DBMS_UTILITY.FORMAT_ERROR_STACK;

 

2015-08-13

[Oracle]Create Sequence

 參考資料:http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6015.htm#SQLRF01314

CREATE SEQUENCE [ schema. ] sequence
   [ { INCREMENT BY  integer }
   | { START WITH  integer }
   | { MAXVALUE integer | NOMAXVALUE }
   | { MINVALUE integer | NOMINVALUE }
   | { CYCLE | NOCYCLE }
   | { CACHE integer | NOCACHE }
   | { ORDER | NOORDER }
   ]…
;

create sequence Sequence_Name
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 2
cycle;

說明
MINVALUE:最小起始號
MAXVALUE:最大結束號
START WITH:下一個取號
INCREMENT BY:每次增加
CYCLE:取到最大值後, 是否再循環由最小值開始
CACHE:先暫存取號數量,預設2(最大值似乎依照版本不同不一樣)
ORDER:是否依照順序取號
NOORDER:Specify NOORDER if you do not want to guarantee sequence numbers are generated in order of request. This is the default.

2015-01-05

[Oracle]Stored Procedure與Stored Function觀念

 參考網站

Stored Routines包含Stored Procedure、Stored Function(Stored Routines似乎只有在MySQL的觀念)

Stored Procedure

  • 傳入值可有可無
  • 回傳值可有可無
  • 執行方式

     

    BEGIN
        PROCEDURE_NAME;
    END;
  • 語法架構

     

    CREATE [OR REPLACE] PROCEDURE proc_name [list of parameters]
    IS 
     Declaration section
    BEGIN   
     Execution section
    EXCEPTION   
     Exception section
    END;

     

Stored Function

  • 傳入值必要
  • 回傳值必要(可以回傳null)
  • 執行方式

     

    SELECT FUNCTION_NAME FROM DUAL
  • 語法架構

     

    CREATE [OR REPLACE] FUNCTION function_name [parameters]
    RETURN return_datatype; 
    IS 
     Declaration_section 
    BEGIN 
     Execution_section
     Return return_variable; 
    EXCEPTION 
     exception section 
     Return return_variable; 
    END;

     

2014-10-17

[Oracle]沒有參數Store Procedure

測試資料
select username,email from eflow.mem_geninf
建立資料表
CREATE TABLE yulin20101017(
    P_username varchar2(100),
    P_email varchar2(100)
)
建立Store Procedure
CREATE OR REPLACE PROCEDURE yulin_sp4
IS
BEGIN
INSERT INTO yulin20101017 (P_username,P_email)
select username,email from eflow.mem_geninf;
end;
執行Store Procedure
DECLARE
begin
yulin_sp4();
commit;
end;
select * from yulin20101017;
delete from  yulin20101017;
drop table yulin20101017;
DROP PROCEDURE yulin_sp4;

2014-09-15

[Oracle]有參數Stored Procedures

1、建立測試Table
CREATE TABLE Yulin ( 
    V1 VARCHAR2(30 BYTE),
    V2 VARCHAR2(50 BYTE),
    V3 VARCHAR2(100 BYTE), 
    N1 NUMBER(30),
    N2 NUMBER(30),
    N3 NUMBER(30),
    D1 DATE,
    D2 DATE,
    D3 DATE 
)
2、建立Stored Procedures
	CREATE OR REPLACE PROCEDURE yulin_sp (P_n1 IN NUMBER,P_n2 IN NUMBER, pv IN CHAR)

	IS

	BEGIN

	INSERT INTO yulin (v1,v2,v3,n1,n2)

	VALUES ('4',pv,'0',P_n1,P_n2);

	END;
3、執行Stored Procedures
	DECLARE

	BEGIN

	yulin_sp (5, 6, 'YT');

	COMMIT;

	END;
4、驗證是否寫入成功 select * from yulin
  









 其他指令 刪除資料:DELETE FROM yulin; 
刪除Procedures:DROP PROCEDURE yulin_sp;