2013年12月6日

TIPTOP GDC 和 CR 對 IE11 的支援

很久沒有主題可以 PO 了,
最近 IE 出 11 版,更新到 11 版時就會發現 CR 列印怎麼沒辦法挑選印表機。
然後 GDC 會出現找不到網頁的錯誤,
只能將相容性原則把 CR 的網址加進去,
但徧徧 IE11 又沒有相容性的符號可以在網址列直接按,
就到功能列去新增吧。

2013年7月24日

SQL 2012 新功能 - Always On

SQL Server 2012 多了一項重要的功能 Always On,
功能類似舊版的資料庫複寫的功能,差異在於舊版只能複寫 1 台 SQL Server,
且複寫資料庫只能在回復模式,不能進行讀取和備份,
在 SQL Server 2012 的 Always On 就提供了群組的功能,可以同時或非同時的複寫資料庫到多台 SQL Server 。
和 SQL Server Cluster 的功能差異在於 Always On 是針對資料庫,Cluster 是針對 Instance 做容錯轉移,各有各的優缺點。

架構:

LAB:SQL Server 2 台,分別是 SQL3、SQL4。

在主機上分別安裝容錯轉移叢集的功能。

開始容錯轉移叢集,建立新的叢集,並將 SQL3、SQL4 加入到叢集裡面。

再來就是安裝獨立的 SQL Server 功能,先選擇全部安裝。
安裝就是一直下一部就完成了,就不多做說明。

開啟 SQL Server 組態管理員,將 Always On 功能開啟,會自動抓取容錯轉移群組的名稱。
記得 2 台都要設定喔。


在建立 Always On 群組之前記得先建行完整備份,然後建立 Always On 的群組,並建立群組的監聽程式。
群組監聽程式主要在於用戶端只要記得這個電腦的名稱,就會連接到主要的 SQL Server,就不用再更新 SQL Server 的 IP 或 電腦名稱。
建立好就會下圖所示:

再來就是修改次要伺服器能不能進行讀取的動作,改為僅限讀取意圖,有一些報表的作業就可以指到次要的伺服器,分散 SQL Server 的效能。

 在容錯轉移叢集就可以看到群組監聽程式所產生的電腦名稱和 Always On 的群組應用程式名稱。

再來就是測試看看會不會自動移轉了,把主要的 SQL Server 關機試試,看能不能用 sql-g1 是否能夠正常連線,這樣就完成啦。

2013年7月20日

測試 SQL Server 2012 Cluster 的 Failover 轉移

之前的 SQL Server 就有 Cluster 叢集的功能了,
在 SQL Server 2012 也是一樣的架構沒有多大的改變,不過可以不需 DTC 的服務,
但是本次還是一樣安裝 DTC 的功能做測試,
此次要測試的就是二台 SQL Server 主機,DB 的檔案放在 iSCSI 的設備,進行 Failover 的轉移,
在 OS 的部份要注意必須安裝 Enterprise 的版本以上才有此功能。
在 SQL Server 2012 提供 Alaways On 的新功能,架構為 SQL Server Cluster 有各自的 DB Data File,
主要的 SQL Server 即時或非即時的鏡象到其他多台次要 SQL Server (舊版的 DB Mirroring 只能一台),
次要的 SQL Server 可以進行報表或備份而不影響使用效能,且分散查詢的使用負載。
叢集安裝方式一樣,只差在 SQL Server 是獨立安裝,下一篇會再說明 LAB 記錄 。

LAB架構:

Host OS:Hyper-V 主機,新增功能檔案存取服務 iSCSI。
Guset OS:二台 SQL Server 的主機,分別是 SQL1、SQL2。

Host OS 新增檔案存取服務的功能。

再來就是開啟 iSCSI 的虛擬磁碟機,
我建立三個磁碟機分別是 Quorum 仲裁磁碟、DTC 分散式交易協調、Data 存放資料磁碟

Guest OS,安裝 Windows Server 2008 R2。
安裝功能容錯轉移叢集。

2013年7月19日

SQL 語法 WITH - 將 SQL 的查詢當一個臨時的 VIEW 來使用

Oracle 有提供非常有用的 SQL 語法,
要介紹的就是 WITH 的函數,主要的功用就是將 SQL 查詢包進來,類似 VIEW 的功能,
這樣要進行 Update 或是比較複雜的查詢就可以用到。

語法: WITH 臨時的名稱 AS (SQL 查詢語法)

直接看範例,查詢所有製程工單未完工的製程序,所以就要把大於或等於有 WIP 量的製程序都列出來。
with ecm_wip as(
select ecm01 ecm01x,min(ecm03) ecm03x from ecm_file
where (ecm301+ecm302+ecm303-ecm311-ecm312-ecm313-ecm314-ecm316 <> 0 or ecm301+ecm302+ecm303 = 0)
group by ecm01)
select ecm01,ecm03,ecm301,ecm302,ecm303,ecm311,ecm312,ecm313,ecm314,ecm316,ecm315 from ecm_file,ecm_wip
where ecm01 = ecm01x
and ecm03 >= ecm03x
order by ecm01,ecm03

將有 WIP 量不為 0 或是還沒有發料的工單找出來取最小的製程序製作成一個臨時的 View,
然後再 JOIN 進來取等於或大於以後的製程序,這樣是不是就可以簡化許多了。

有了這個 WITH 的語法,我們寫 SQL 就可以將要查詢的資料先各別寫出來,然後再用 WITH 一一的拼裝起來,
最後再全部 JOIN 在一起就可以了,
我們也不需要為了專屬的 SQL 查詢,寫了許多共用的 VIEW 出來,也變的不容易閱讀。

當資料量越來越大時,不佳的查詢 SQL 語句就會影響系統的效能,好用的函數也是可以多加利用,
有複雜的 SQL 語句時記得要查看是否有 Full Scan Table 的情況。

2013年7月18日

Exchange 2013 Client Access role NLB (Network Load Balance) 網路負載平衡

Exchange 2013 Client Access role 和 Exchange 2010 有一些改變,
官方將不再提供 Client Access Array 的功能,將由 Exchange 自動抓取 Active Directory 的 CAS role 最佳的主機來連線。
要做到 NLB 的功能的話 SSL 就會出現問題了,AD 指派的主機就不是 Client Access Array 的 URL 而是 CAS 的主機 URL。
要解決這麼問題最便宜的方法就是重新向 CA 企業根憑証中心註冊一個 *.domain.com 的網頁伺服器憑証,就不需用硬體設備來做叢集。

LAB:
Active Directory 環境。
安裝 Exchange Client Access role 2 台主機,分別為 Exchange-CAS1、Excahnge-CAS2。

安裝就不多加說明了,參考微軟官方網站說明下指令就可以了,
http://technet.microsoft.com/zh-TW/library/bb691354(v=exchg.150).aspx

下載 Exchange 2013 CU1 完整安裝:http://www.microsoft.com/zh-TW/download/details.aspx?id=38176
解壓縮後就可以安裝了,安裝就不多說明了,只要選擇 Client Access role。

Exchange 2013 現實 DAG Mailbox 高可靠性的功能

Exchange 2013 在服務的角色將 Transport role 拿掉合併到 Mailbox role 了。
要測試的就是 Exchange 2013 Mailbox role 的 DAG (Database Availablility Group) 的功能。
功能和 Exchange 2010 相同,架構和模式也都一樣沒有改變。
要把現有的 Exchange 2010 升級到 Exchange 2013 需將 Exchange 2010 升級到 Service Pack 3,
然後 Exchange 2013 需要安裝 CU1 以上的版本,才能 Exchange 2013 和 Exchange 2010 混合使用。
有 Edge role 的情況時,也必須要升級到 Exchange 2010 的 SP3 。

LAB 環境:
Active Directory 環境下。
Exchange 2013 Mailbox role 2 台,分別是 Exchange-MBX1、Exchange-MBX2。
Exchange 2013 Client Access role 1 台,Exahnge-CAS1。

安裝就不多加說明了,參考微軟官方網站說明下指令就可以了,
http://technet.microsoft.com/zh-TW/library/bb691354(v=exchg.150).aspx

下載 Exchange 2013 CU1 完整安裝:http://www.microsoft.com/zh-TW/download/details.aspx?id=38176
解壓縮後就可以安裝了,安裝就不多說明了,只要選擇 Mailbox role。

必須要有 Client Access role 的主機,因為 Exchange 2013 管理介面改為網頁的樣式,
所以必須要有 CAS 主機才能夠開啟管理介面去設定。

2013年5月22日

Oracle 分析統計函數 - OVER 累加、LAG 上一筆、LEAD 下一筆

Oracle 的資料庫在使用率之所以翌立不搖,就是因為穩定、效能高、維護容易,
再來就是提供 200 多種的相關 SQL 語法。
參考 Mastering Oracle SQL and SQL Plus 的書籍,就有提到進階的 Oracle SQL 指令。

分析統計的指令 OVER 說明如下:
SELECT 統計函數(欄位) OVER (window spec) FROM table
其實可以把 OVER 當作同一個條件下的子查詢,並且有 Current Row 的概念。
整個 table 的資料在某些條件下篩選的資料就是 window,也就是我們所下 where 條件出來的資料。

統計函數就不多說明了,一般就是 SUM、AVERAGE、MIN、MAX…

windows-spec 指令的語法就是: partition by 欄位 + order by 欄位 + range-spec
1. partiton by:就是區分成多個區段做分析運算
2. order by:要先跟 Database 說依什麼方式的順序來計算,所以就要在此區段來定義
3. range-spec:要計算的資料範圍,下面會再做說明

範例要累加料件庫存的數量:
依料件不同各別累加,累加的順序為 img01,img02,img03,img04
select img01,img02,img03,img04,img10,sum(img10) over (partition by img01 order by img01,img02,img03,img04) from img_file
where img10 > 0
order by img01,img02,img03,img04

range-spec 說明:
RANGE + BETWEEN 開始 AND 結束
RANGE + UNBOUNDED PRECEDING
ROW + BETWEEN 開始 AND 結束
ROW + UNBOUNDED PRECEDING

BETWEEN…AND…:開始或結束,可以用 CURRENT ROW(目前)、PRECEDING(往前)、FOLLOWING(往後)

上面的範例再加上資料的範圍,結果會是相同的,依料件不同各別累加,累加的順序為 img01,img02,img03,img04
select img01,img02,img03,img04,img10,sum(img10) over (partition by img01 order by img01,img02,img03,img04 range unbounded preceding) from img_file
where img10 > 0
order by img01,img02,img03,img04
或是
select img01,img02,img03,img04,img10,sum(img10) over (partition by img01 order by img01,img02,img03,img04 row between unbounded preceding and current row) from img_file
where img10 > 0
order by img01,img02,img03,img04

範例加總往前1筆到往後1筆的數量:
select img01,img02,img10,
sum(img10) over (partition by img01 order by img01,img02 rows between 1 preceding and 1 following) 
from img_file
where img10 > 0
order by img01,img02
要注意,當用 BETWEEN…AND…超過 partition by 的運算範圍的時候,partition by 就不會有作用。

當然也可以做到是統計數字是往下累加(遞增)的,還是往上累加(遞減)的方式。

統計函數還有提供 LAG 上一筆、LEAD 下一筆,想要比較上一筆或下一筆的資料就可以做資料的判斷。
範例為帶出上一筆的庫存數量:
select img01,img02,img10,lag(img10) over (partition by img01 order by img01,img02)
from img_file
where img10 > 0
order by img01,img02

如果在 SQL 想要能夠抓取上一筆的欄位或是下一筆的欄位資料,也是可以用 OVER 的方式來達到。

2013年5月17日

利用拖拉的功能,實現資料排序、複製或移除

在 Genero 有提供 Drag & Drop 功能,也就是使用滑鼠(或觸控螢幕)來進行資料的搬移。
這樣就可以調整資料的順序,或是將資料由 A 搬到 B ,或者可以把資料做移除的動作。

DEFINE l_dd ui.DragDrop 要先宣告螢幕拖拉的變數
必須要在 DISPLAY ARRAY 再加上 Drag 和 Drop 的區段,使用方法如下:
ON DRAG_START(l_dd) :當開始拖拉來源端 Object 的某一筆資料時
ON DRAG_FINISHED(l_dd):當來源端的 Object 拖拉完成時
ON DRAG_ENTER(l_dd):當拖拉到目的端 Object 時
ON DRAG_OVER(l_dd):當拖拉到目的端 Object 的某一個 Cursor 時
ON DROP(l_dd):當拖拉到目的端 Object 完成時

所以要在 DISPLAY ARRAY 可以進行拖的動作就要加上 DRAG ,要可以進行拉的動作就要加上 DROP ,不然滑鼠就會出現禁止的圖示而無法完成拖拉。

再來就是相關的指令說明:
1. setOperation:預設的拖拉動作
    CALL l_dd.setOperation(""):預設不能拖拉,其實你覺得拖拉很討厭的話,可以執行此指令
    CALL l_dd.setOperation("move"):預設為資料搬移
    CALL l_dd.setOperation("copy"):預設為資料複製

2. addPossibleOperation:額外增加拖拉的動作
    CALL l_dd.addPossibleOperation("copy"):通常都是預設設定為搬移,然後拖拉時加上 Ctrl 鍵,就可以 copy 的動作

3. setMimeType:設定 MIME 的格式,當要拉到外部的程式時,必須要可以支援拖拉的程式才行(記事本就沒這個功能了)
    CALL l_dd.setMimeType("text/plain")
    CALL l_dd.setMimeType("text/uri-list")
    CALL l_dd.setMimeType("text/x-vcard")
                         :
    (自定義的 MIME 格式)

4. setBuffer:設定拖資料時暫存的資料
    CALL l_dd.setBuffer(g_sfa[arr_curr()].sfa03):舉例要把 sfa03 的欄位內容拖拉的方式複製到 Excel 來貼上

5. setFeedback:設定拖拉的模式為取代或是新增
    CALL l_dd.setFeedback("all") :可取代也可以新增
    CALL l_dd.setFeedback("insert"):只能新增
    CALL l_dd.setFeedback("select"):只能取代

6. getLocationRow() :回傳目前要拖拉的 cursor 行號
    LET l_ac = l_dd.getLocationRow():舉例目前要拖拉的 cursor 行號指派給 l_ac 變數

7. getLocationParent():回傳 Tree View 的 Node,用法和 getLocationRow() 相同

8. getOperation():回傳拖拉動作是搬移還是複製
    CASE l_dd.getOperation()
          WHEN "move"
                 MESSAGE "MOVE"
          WHEN "copy"
                 MESSAGE "COPY"
    END CASE

9. selectMimeType():所拖拉的資料格式
    CASE
            WHEN dnd.selectMimeType("text/plain") 
            WHEN dnd.selectMimeType("text/uri-list") 
    END CASE

範例為要把單身的資料用拖拉的方式進行資料的排序
DEFINE   l_dd      ui.DragDrop,
              l_n_old   LIKE type_file.num5,
              l_n_new   LIKE type_file.num5
DEFINE   l_i       LIKE type_file.num5
DISPLAY ARRAY g_oeb TO s_oeb.* ATTRIBUTE(COUNT=g_rec_b) 
         ON DRAG_START(l_dd)
            LET l_n_old = arr_curr()
            CALL l_dd.setOperation("move")
         ON DRAG_FINISHED(l_dd)
            IF l_n_old <> l_n_new THEN
               IF l_n_old < l_n_new THEN
                  FOR l_i = l_n_old+1 TO l_n_new 
                      LET g_oeb[l_i].oeb03n= l_i-1
                  END FOR 
                  LET g_oeb[l_n_old].oeb03n= l_n_new
               ELSE 
                  FOR l_i = l_n_new TO l_n_old-1
                      LET g_oeb[l_i].oeb03n= l_i+1
                  END FOR 
                  LET g_oeb[l_n_old].oeb03n= l_n_new
               END IF 
               FOR l_i = 1 TO g_tc_sfb1.getlength()
                   UPDATE oeb_file SET oeb03= g_oeb[l_i].oeb03n
                    WHERE oeb01 = g_oea.oea01
                      AND oeb03 = g_oeb[l_i].oeb03
               END FOR 
            END IF
            CALL DIALOG.setCurrentRow("s_oeb",l_n_new)     # 將 cursor 移到新的位置
            CALL t400_b_fill()                                                     # 更新資料
            CONTINUE DISPLAY 
         ON DRAG_OVER(l_dd)
            LET l_n_new = l_dd.getLocationRow()
         ON DROP(l_dd)
END DISPLAY
此範例只提供參考用,要注意欄位是否為 INDEX 有重複的情況,會造成 update 失敗。

結論:拖拉方式的操作在某些情況可以帶來使用者更方便的使用,試試看吧~~

2013年5月16日

Linux 檔案同步,AP主機程式保持一致

當有二台 TIPTOP 主機提供 AP 服務時,希望程式修改後也能夠定期同步到另一台主機,
這時候就可以用 rsync 的指令,快速的把不相同的檔案複製到另一台主機上,權限和檔案屬性也不會改變。

rsync 的指令就不加以敘述,請參考其他的網站的說明。

同步指令如下:
rsync -av -e ssh root@192.168.1.100:/u1/topprod /u1
命令的說明是用 ssh 以 root 帳號登入到 192.168.1.100 主機,然後將 /u1/topprod / 目錄的所有檔案同步到此主機的 /u1 目錄。
記得目的端就不要再加上 topprod 不然會建立新的資料夾。

因為不同主機 IP 位置不同,所以必須排除 tiptop_env 檔案,所以再加上 --exclude 後面加上相對的路徑名稱。
不能加上絕對的路徑,會沒辦法排除。
rsync -av -e ssh --exclude tiptop/bin/tiptop_env root@192.168.1.100:/u1/topprod /u1
再來就輸入來源主機的 root 的密碼就開始進行檔案同步。

如果檔案很多的話,需建立 list 檔來排除。
建立 vi /root/sync2.list 檔,排除以下的檔案。
tiptop/bin/tiptop_env
tiptop/lib/4gl/cl_user.4gl
tiptop/lib/42m/lib_cl_user.42m
tiptop/lib/42m/lib_cl_user.4gl
tiptop/lib/42m/lib.42x

然後再加上 --exclude-from 參數來排除指定的 list 檔案,
rsync -av -e ssh --exclude-from=/root/sync2.list root@192.168.1.100:/u1/topprod /u1

如果希望能夠排程的同步檔案,必須要先讓 ssh 不需要密碼就可以登入。
在目的主機 /root 目錄建立 .ssh 的資料夾, mkdir /root/.ssh 。
到 .ssh 目錄執行 ssh-keygen -d ,會產生二個檔案 id_dsa 和 id_dsa.pub 。
然後再到來源主機,一樣建立相同的資料夾 /root/.ssh 。
將 id_dsa.pub 檔案傳送到來源主機,並更名為 authorized_keys2 。
執行:scp id_dsa.pub 192.168.1.100:/root/.ssh/authorized_keys2

測試看看是否不需輸入密碼:ssh root@192.168.1.100
如果還是會跳出密碼的驗証,請修改 authorized_keys2 權限改為 640 (chmod 640 authorized_keys2 )

再來就是建立一個檔名為 sync2 批次檔。
vi  /root/sync2
rsync -av -e ssh --exclude-from=/root/sync2.list root@192.168.1.100:/u1/topprod /u1
檔案權限改為 700  (chmod 700 sync2),可執行檔。

執行看看是否成功:./sync2

再來就是加到 cron job 每 5 分鐘同步一次。
執行 crontab -e 編輯排程檔:
加上:*/5 * * * * /root/sync2

這樣每 5 分鐘就會將來源主機和目的主機有差異的檔案進行複製的動作。
這樣AP主機的程式就會保持一致。

2013年5月9日

鼎新所提供的 Java Mail 的功能

鼎新提供 TIPTOP 可以寄送 Mail 的功能,
也有許多程式也已經有把程式碼都加進去裡面。
所以備忘一下,日後比較方式使用。

先維護 xml 檔:/u1/topprod/tiptop/ds4gl2/bin/javamail/genxml
維護 mail server 主機、SMTP Port、驗証使用者名稱、密碼…等。

再來就是 4GL 的部份:
g_xml.subject:郵件主旨

g_xml.body:郵件內容的檔案路徑,所以要先把內容存成一個 Temp File 的方式
將郵件的內文存到 Temp File:
LET l_buf1 = FGL_GETENV("TEMPDIR")
LET l_buf1 = l_buf1,"/report_context_" || FGL_GETPID() || ".txt"
LET l_buf = "echo '" || l_buf || "' > " || l_buf1
RUN l_buf WITHOUT WAITING
LET g_xml.body = l_buf1

g_xml.sender:寄件者 Mail Address:寄件者名稱 (中間用冒號 : 區隔)

g_xml.recipient:收件者 Mail Address:收件者名稱 (中間用冒號 : 區隔),多個用收件件用分號 ; 來區別
g_xml.ccrecipient:複本 Mail Address:收件者名稱 (中間用冒號 : 區隔),多個用收件件用分號 ; 來區別
g_xml.bccrecipient:密件複本 Mail Address:收件者名稱 (中間用冒號 : 區隔),多個用收件件用分號 ; 來區別

g_xml.attach:附件的檔案位置,多個附件用分號 ; 來區別

CALL cl_jmail():發送 e-mail

就可以把 e-mail 透過鼎新所提供的 Java Mail 寄送出去,
收件者建議在 aooi998 進行維護,寄件者可以抓 gen_file 資料。

郵件的內容為 HTML 的格式,所以如果是資料的話可以用 TABLE 的方式來呈現。
<TABLE BORDER=1 WIDTH=400px STYLE=border-collapse:collapse; BORDERCOLOR=black>
<TR align=center><TD>訂單+項次</TD><TD>原交期</TD><TD>新交期</TD></TR>
<TR><TD>g_oea.oea01</TD><TD>g_oeb.oeb15</TD><TD>g_oeb.oeb16</TD></TR>
</TABLE>

進階的 Dialog 說明-可多重選取的模式

先前有討論過更便利的 DIALOG - 同時單頭單身查詢/輸入的 Multiple Dialogs 功能,
可以同時進行多個 Display、Input、Display Array、Input Array 的操作。
Genero 不斷的加強 Dialog 的功能,提供更方便的操作使用。
像是 Input 和 Display Array 在同一個 Dialog 時,就可以輸入資料,然後同步 Display Array 一直 Insert 或是 Append 資料,
在 Display Array 選擇時,也同步顯示資料在 Input ,可以快速進行資料的修改,
就不用局限一定要輸入或維護完一筆資料後就異動一次資料庫了,全部資料維護完後再一次更新到資料庫。
另外還提供多重選擇的功能,搭配之前在畫面加上資料清單,從此不必再下一筆查了的方式,
就可以讓使用者選擇多筆資料後,按下過帳或是確認的按鈕,就可以一次完成所有的動作。

Dialog 的函數說明:
CLASS Method:
1. getCurrent():回傳目前是在那一個 Object 的操作。
2. setDefaultUnbuffered(boolean):設定取消 Buffer ,才可以修改資料。
Object Method:
1. accept():確認所有的欄位是否完整。
2. insertNode(Object Name, 第幾個 Node):新增 Tree View 的 Node
3. appendNode(Object Name, 第幾個 Node):增加 Tree Viw 的 Node
4. deleteNode(Object Name,第幾個 Node):刪除 Tree Viw 的 Node
5. insertRow(Object Name,第幾行):新增 Object 畫面上的一行紀錄
6. appendRow(Object Name):增加 Object 畫面上的一行紀錄
7. deleteRow(Object Name, 第幾行 ):刪除 Object 畫面上的一行紀錄
8. deleteAllRows(Object Name):刪除 Object 畫面上的所有紀錄
9. getArrayLength(Object Name):回傳 Object 的筆數
10. getCurrentItem( ):回傳目前 Object 的在那一個欄位、List、Action
11. getCurrentRow(Object Name):回傳目前在那一個 Object 上的第幾行
12. getFieldBuffer(Field Name):回傳 Construct 或是 Input Buffered Mode 的 Buffer 值
13. getFieldTouched(Field List):回傳欄位有動到的清單
14. setFieldTouched(Field List,boolean):設定或取消欄位的 Touch Flag 的值來判斷是否欄位有動過
15. setArrayLength(Object Name,數量):設定總共 Object 的行數
16. getForm():回傳目前的 Form 名稱
17. nextField(Field Name):將游標跳到下一個欄位
18. setActionActive(Object,bollean):設定 Object 的 Action 是否要開啟或關閉
19. setActionHidden(Object Name,bollean):設定 Object 的 Action 是否要顯示或是隱藏
20. setCurrentRow(Object Name,第幾行):跳到所指定的第 n 行
21. setFieldActive(Field List,bollean):設定欄位是否可以編輯
22. setCellAttributes(Object Array):設定欄位的屬性(單一個 Dialog)
23. setArrayAttributes(Object Name,Object Array):設定欄位的屬性(多個 Dialog)
24. validate(Field List):驗証欄位是否為必須輸入、Not Null 或是 Validation Rules 的規則
25. isRowSelected(Object Name,第幾行):判斷第幾行是否有選取反白
26. selectionToString(Object Name):回傳所選擇的資料行的所有欄位內容
27. setSelectionMode(Object Name,bollean):開啟或關閉是否可以多筆的功能,0 為單選,1為多選
28. setSelectionRange(Object Name,開始行,結束行,選或不選):設定開始行到結束行要 1 為選取或是 0 為取消選取,結束行是 -1 表示為最後一行

範例多重選擇後然後將所選取的全部確認:
DISPLAY ARRAY g_oea_l TO s_oea_l.* ATTRIBUTE(COUNT=g_row_count)
   BEFORE DISPLAY
       CALL DIALOG.setSelectionMode( "s_oea_l", 1 )
   ON ACTION confirm
       LET g_action_choice="confirm"
       IF cl_chk_act_auth() THEN
          IF cl_confirm('axm-351') THEN
             FOR l_i=1 TO g_row_count
                IF DIALOG.isRowSelected( "s_oea_l", l_i) THEN
                     LET g_oea.oea01 = g_oea_l[l_i].oea01_l
                     CALL i101_y()
               END IF
            END FOR
         END IF
      END IF
      CONTINUE DIALOG
END DISPLAY

2013年4月22日

GDC 提供 Windows DDE 的支援

Dynamic Data Exchange (DDE) 就是二個不同的應用程式,能夠彼此相互共享記憶體的資料,
讓資料可以即時更新,不同應用程式資料保持一致。
MS Office 有提供 DDE 的功能,以一般常用的 Excel 來說明,
當 TIPTOP 資料進行修改的時候,Excel 資料也會跟著馬上進行同步的修改。

舉例 Excel DDE 語法如下說明:
CONSTANT file = "Sheet1"
CONSTANT prog = "EXCEL"
DEFINE val, rval STRING
DEFINE res INTEGER
CALL ui.Interface.frontCall("WINDDE","DDEConnect", [prog,file], [res] ) 開啟 Excel DDE
CALL ui.Interface.frontCall("WINDDE","DDEPoke", [prog,file,"R1C1",val], [res] ); 將 val 值傳送給 Excel 第1行第1欄
CALL ui.Interface.frontCall("WINDDE","DDEPeek", [prog,file,"R1C1"], [res,rval] ); 接收 Excel 第1 行第1欄的 rval 值
CALL ui.Interface.frontCall("WINDDE","DDEError",[],[mess]); # 顯示錯誤訊息
CALL ui.Interface.frontCall("WINDDE","DDEExecute", [prog,file,"[save]"], [res] ); 將 Excel 存檔
CALL ui.Interface.frontCall("WINDDE","DDEFinish", [prog,file], [res] ); 關閉此 DDE
CALL ui.Interface.frontCall("WINDDE","DDEFinishAll", [], [res] ); 關閉所有的 DDE

要注意,要啟動 Excel DDE 時,必須先開啟 Excel 且有 Sheet1 才可以使用。
可以使用 Windows Path 或是用絕對路徑方式,用 cl_open_prog("excel","/p c:\\tiptop") 來開啟。

範例,程式讀取 Excel 的料號,並將品名和規格回傳給 Excel 。
#宣告
CONSTANT file = "Sheet1"
CONSTANT prog = "EXCEL"
DEFINE val, rval,colrow STRING
DEFINE res INTEGER

#讀取 Excel
CALL ui.Interface.frontCall("WINDDE","DDEConnect", [prog,file], [res] )
WHILE TRUE
   LET l_ac = l_ac + 1
   LET colrow = "R",l_ac USING '<<<<<',"C1"
   CALL ui.Interface.frontCall("WINDDE","DDEPeek", [prog,file,colrow], [res,rval] );
   LET g_ima[l_ac].ima01 = rval
   LET colrow = "R",l_ac USING '<<<<<',"C2"
   CALL ui.Interface.frontCall("WINDDE","DDEPeek", [prog,file,colrow], [res,rval] );
   LET g_ima[l_ac].ima02 = rval
   LET colrow = "R",l_ac USING '<<<<<',"C3"
   CALL ui.Interface.frontCall("WINDDE","DDEPeek", [prog,file,colrow], [res,rval] );
   LET g_ima[l_ac].ima021 = rval
   IF cl_null(g_ima[l_ac].ima01) THEN
      EXIT WHILE
   END IF
END WHILE

# 回傳到 Excel
LET val = g_ima[l_ac].ima01
LET colrow = "R",l_ac USING '<<<<<',"C1"
CALL ui.Interface.frontCall("WINDDE","DDEPoke", [prog,file,colrow,val], [res] );
LET val = g_ima[l_ac].ima02
LET colrow = "R",l_ac USING '<<<<<',"C2"
CALL ui.Interface.frontCall("WINDDE","DDEPoke", [prog,file,colrow,val], [res] );
LET val = g_ima[l_ac].ima021
LET colrow = "R",l_ac USING '<<<<<',"C3"
CALL ui.Interface.frontCall("WINDDE","DDEPoke", [prog,file,colrow,val], [res] );

#存檔
CALL ui.Interface.frontCall("WINDDE","DDEExecute", [prog,file,"[save]"], [res] );

# 關閉 DDE
CALL ui.Interface.frontCall("WINDDE","DDEFinish", [prog,file], [res] );

# 關閉所有 DDE
CALL ui.Interface.frontCall("WINDDE","DDEFinishAll", [], [res] );

因 Genero 的畫面不像 Excel 隨時偵測記憶體並更新顯示資料,TIPTOP 畫面只能下達 DISPLAY 指令手動更新資料,
所以用 TIPTOP 程式來更新 Excel 的資料對此 DDE 的功能使用上或許會比較適合,
或是將 TIOTOP 資料匯出 Excel 的功能並寄送 e-mail 等功能會實用一點。

利用 Client 端 GDC 發送 e-mail

現今 e-mail 已是公司通知不可獲缺的工具,
在 GDC 有提供寄送 e-mail 的功能,直接使用者的電腦寄送 e-mail 。
當使用者執行某一個動作時,就可以發送 e-mail 給相關的人員,可做為溝通連絡的用途。
舉例公司就是當修改料件製程時,有工單生產此料件還未結案時,就發通知給相關人員做檢查。

GDC 是屬於 Client 端的應用程式,所以 e-mail 是由 Client 端來發出,
可以透過 MAPI (如 Outlook) 或是 SMTP (郵件伺服器) 方式寄送。

語法說明:
需要注意 l_buf1、l_buf 變數必需定義為 STRING ,不能為 varchar 的型態,l_result、l_id 為 INTEGER 型態。

CALL ui.Interface.frontCall("WinMail", "Init", [], [l_id])     宣告。
CALL ui.interface.frontCall("WinMail", "SetSubject", [l_id, l_buf], [l_result])    設定 e-mail 主旨
CALL ui.interface.frontCall("WinMail", "SetBody", [l_id, l_buf], [l_result])       設定 e-mail 的內容
CALL ui.Interface.frontCall("WinMail", "AddTo", [l_id, l_buf1, l_buf], [l_result])    設定多個收件者名稱、e-mail

CALL ui.Interface.frontCall("WinMail", "AddCC", [l_id, l_buf1, l_buf], [l_result])   設定多個複本收件者名稱、e-mail

CALL ui.Interface.frontCall("WinMail", "AddBCC", [l_id, l_buf1, l_buf], [l_result])   設定多個密件複本收件者名稱、e-mail

CALL ui.Interface.frontCall("WinMail", "AddAttachment", [l_id, "c:\\mydocs\report.doc"], [result])  設定多個附件


GDC 有二個方式寄送 e-mail ,透過 SMTP 直送發送 e-mail 或是開啟 Client 郵件軟體(如 Outlook)
A. MAPI 透過使用者端的郵件軟體來發送。
CALL ui.Interface.frontCall("WinMail","SendMailMAPI", [l_id], [l_result] )    

B. SMTP 直接傳送郵件到郵件主機。
CALL ui.Interface.frontCall("WinMail", "SetFrom", [l_id, l_buf1, l_buf], [l_result])    設定 e-mail 的寄件者名稱、e-mail
CALL ui.Interface.frontCall("WinMail", "SetSmtp", [l_id, "192.168.1.100"], [l_result])    設定 SMTP Server
CALL ui.Interface.frontCall("WinMail", "SendMailSMTP", [l_id], [l_result])    利用 SMTP 發送 e-mail

CALL ui.Interface.frontCall("WinMail", "GetError", [l_id], [l_str])     # 錯誤訊息
CALL ui.Interface.frontCall("WinMail", "Close", [l_id], [l_result])    # 關閉

雖然提供 Client 端的 e-mail 功能,不過使用上就顯的很陽春,只能用純文字發送 e-mail ,不能改為 HTML 樣式,也不能修改 e-mail 的編碼,所以中文字就不能顯示,再來就是 SMTP 沒有提供帳號驗証的功能,必須公司內部郵件主機是不需身份驗証的,所以使用此功能要特別注意。

在 TIPTOP 設定 e-mail 的收件者,儘可能在 aooi998 來設定,日後比較方便維護和紀錄。

2013年4月9日

Oracle 發送 e-mail 的功能

有些時候能夠定期由 Oracle 來寄送一些資料庫的狀況和資訊或是建立 Alert 機制,
對於 DBA 來說工作就可以輕鬆不少,也可以避免一些預期上的問題。
或是有一些報表或是執行的結果也可以透過這個方式,將  SQL 結果寄送到相關人員的 e-mail 信箱。

Oracle 提供 e-mail 傳送的功能 UTL_SMTP 來發送郵件,
只要放到 Oracle 排程裡就會定期發送 e-mail 給相關的人員,
使用此功能必須要以 sysdba 的角色來登入才能使用 (用 sys 的帳號) 。

指令看了就知道作用了,就不詳細介紹。

範例:發送 Oracle Tablespace 可用容量的 e-mail 。

declare
 --宣告
  l_mail_conn   UTL_SMTP.connection;
  l_boundary    VARCHAR2(50) := '----=*#abc1234321cba#*=';
BEGIN
 --定義郵件主機,不能用 IP 只能用 host name,可以在 /etc/hosts 增加
  l_mail_conn := UTL_SMTP.open_connection('mailserver', '25');
  UTL_SMTP.helo(l_mail_conn, 'mailserver');

  --寄件者
  UTL_SMTP.mail(l_mail_conn, '4shiun@gmail.com');

  --多個收件者
  UTL_SMTP.rcpt(l_mail_conn, '4shiun@gmail.com');

  UTL_SMTP.open_data(l_mail_conn);

  --以 HTML 方式來傳送,定義收件者和寄件者名稱
  UTL_SMTP.write_data(l_mail_conn, 'Date: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'To: ' || 'Adam' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'From: ' || 'Adam' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Subject: ' || 'Tablespace Information' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Reply-To: ' || 'Adam' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'MIME-Version: 1.0' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/alternative; boundary="' || l_boundary || '"' || UTL_TCP.crlf || UTL_TCP.crlf);

  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/html; charset="iso-8859-1"' || UTL_TCP.crlf || UTL_TCP.crlf);

  --把 Tablespace 的資料用 Table 來顯示
  UTL_SMTP.write_data(l_mail_conn, '<table border=1 width=800px>');
  UTL_SMTP.write_data(l_mail_conn, '<TR align=center><TD>tablespace_name</TD><TD>free</TD><TD>used</TD><TD>total</TD><TD>used_percent</TD><TD>free_percent</TD></TR>');
  for i in(select a.tablespace_name,to_char(b.free,'fm999,999,999,999') free,to_char(a.total-b.free,'fm999,999,999,999') used,to_char(a.total,'fm999,999,999,999') total,to_char(((a.total - b.free)/a.total)*100,'999.99')||'%' used_percent,to_char((b.free/a.total)*100,'999.99')||'%' free_percent from (select tablespace_name,sum(bytes) total from dba_data_files group  by tablespace_name)a,(select tablespace_name,sum(bytes) free from dba_free_space group by tablespace_name) b where a.tablespace_name= b.tablespace_name order by 1)
      loop
      UTL_SMTP.write_data(l_mail_conn, '<TR align=right><TD align=left>'||i.tablespace_name||'</TD><TD>'||i.free||'</TD><TD>'||i.used||'</TD><TD>'||i.total||'</TD><TD>'||i.used_percent||'</TD><TD>'||i.free_percent||'</TD></TR>');
      end loop;
  UTL_SMTP.write_data(l_mail_conn, '</table>');
  UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);

  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary || '--' || UTL_TCP.crlf);
  UTL_SMTP.close_data(l_mail_conn);

  UTL_SMTP.quit(l_mail_conn);
END;

發送 e-mail 當然也可以把 Blob 欄位的資料當成附件的方式來傳送。
要注意的是,如果 mail server 有認證才能發送 e-mail 的話,就需要再加上帳號和密碼,
發送的 e-mail 如果是中文的話,HTML 的文字編碼也需要修改。
建議是把此 SQL 指令寫成 PROCEDURE 的方式方便執行。

2013年4月2日

SQLCA.SQLCODE 和 SQLCA.SQLERRD 和 STATUS 錯誤碼查詢

要判斷程式在執行的時候,SQL 指令回傳結果正確與否,
在 4GL 就提供以下三個變數來使用:

SQLCA.SQLCODE:
執行 SQL 指令後,回傳 Informix 4GL 所表示的 Error Code。

SQLCA.SQLERRD:
SQLERRD[1]~SQLERRD[6],SQLERRD[2] 表示資料庫的錯誤訊息,SQLERRD[3] 表示執行的 row 數,其他特別功能使用就不參考。

STATUS:
通常會等於 SQLCA.SQLCODE ,但是多了系統的錯誤碼,所以執行 SQL 指令後系統 I/O 錯誤時就會顯示另一個錯誤碼。

程式執行時會回傳 SQLCA.SQLCODE 訊息碼,有以下三種情況:
0 表示執行成功。
100 表示成功但無資料,所以當 Update 沒有符合條件的資料可以用。
<0 負數表示有錯誤。

當訊息碼為負數時,表示為錯誤訊息,但是我們要怎麼查是什麼錯誤,
只要在命令環境下,執行 finderr 錯誤碼 就會出現錯誤的說明了。

例:執行 finderr -201 會出現以下訊息,英文應該大概上就知道什麼涵義:
-201    A syntax error has occurred.

This is the general error code for all types of mistakes in the form of an SQL statement. Look for missing or extra punctuation (for example, missing or extra commas, omission of parentheses around a subquery, etc.), keywords misspelled (for example VALEUS for VALUES), keywords misused (for example, SET in an INSERT statement, INTO in a subquery), keywords out of sequence (for example a condition of "value IS NOT" instead of "NOT value IS"), or the use of a reserved word as an identifier.
Note: Database servers that support "full NIST compliance" do not reserve any words; queries that work with these database servers may fail with error -201 when used with earlier implementations.

不過因為 SQLCA.SQLCODE 是 Informix 在 4GL 所規範出來的錯誤訊息,所以比較籠統的方式來呈現,舉例 -201 表示語法錯誤,但是沒辦法詳細說明錯誤在那,因此在 Debug 時可以用 SQLERRD[2] 就可以知道 Oracle 的 ORA 的錯誤碼,比較快速的知道語法是那裡的錯誤。

要得知 SQL 執行的筆數 SQLCA.SQLERRD[3] 就可以知道 SQL 指令所處理的筆數,在程式上也可以多加利用。

另外 Genero 還有提供二個指令,要注意不是變數型態的 SQL Error Code 而是指令,所以 Debug 不能顯示內容,
要先 LET 到變數或是 DISPLAY 才看的到,也是可以參考使用,功能和 SQLERRD[2] 相同。

SQLSTATE:
回傳 ANSI SQLSTATE 錯誤碼,現在應該所有商用資料庫都有支援 ANSI SQL。

SQLERRMESSAGE:
回傳資料庫的錯誤碼和錯誤的說明。

2013年3月26日

建立 Oracle 觸發 Trigger 來 Debug

有時候系統會到遇到靈異事件,
經常發生還容易查,用 r.d2+ 就大概可以 debug 出來問題在那,
偶發情況然後又只要重新再做一次又正常了,不知道到底是時間差、還是資料的問題,或是程式的 bug。
系統又沒有記錄的話,使用者刪除或修改資料是不會承認的,都是朝系統的問題來處理。

只好建立 Trigger 來記錄資料的異常歷史記錄了,看到底是誰搞的鬼。
在 TIPTOP 有提供 log_file 和 aooq040 提供 Trigger log 的查詢,但是 Trigger 要自已在 Oracle 建立。

Trigger 的語法:
CREATE OR REPLACE TRIGGER 觸發名稱
BEFORE INSERT OR UPDATE OR DELETE OF 觸發的欄位 ON 觸發的表
AFTER INSERT OR UPDATE OR DELETE OF 觸發的欄位 ON 觸發的表
FOR EACH ROW
記錄異動的資料
END;

bmd_file 異動資料的範例,把異動的欄位記錄在 log_file:
create or replace trigger bmd_file_log
--只記錄4 個欄位的異動
after insert or update or delete of bmd01,bmd02,bmd03,bmd04,bmd08 on bmd_file
for each row
declare l_col varchar2(3);
        l_user varchar2(6);
        l_type varchar2(20);
        l_cnt  number;
begin
l_col :='';
--先找有沒有資料,當執行失敗時 trigger 就會中止,所以要先找是否有資料,SID 是唯一值,SESSION有可能重複。
select count(*) into l_cnt from v$session,gbq_file where process=gbq01 and sid = userenv('sid') and process not like '%:%';
if l_cnt > 0 then
--把TIPTOP使用者和程式代號找出來
   select gbq03,gbq04 into l_user,l_type from v$session,gbq_file where process=gbq01 and audsid = userenv('sessionid') and process not like '%:%';
else
--把非TIPTOP的使用者和程式找出來
   l_user:=sys_context('userenv','os_user'); l_type:=substr(sys_context('userenv','module'),1,10);
end if ;
--新增
if inserting then
   l_col :='ins';
   insert into log_file values('bmd_file',:new.bmd01,:new.bmd02,:new.bmd03,:new.bmd04,:new.bmd08,l_col,sysdate,l_user,l_type,'','');
end if;
--修改就記錄修改前和修改後
if updating then
   l_col :='upd';
   insert into log_file values('bmd_file',:old.bmd01,:old.bmd02,:old.bmd03,:old.bmd04,:old.bmd08,l_col||'-old',sysdate,l_user,l_type,'','');
   insert into log_file values('bmd_file',:new.bmd01,:new.bmd02,:new.bmd03,:new.bmd04,:new.bmd08,l_col||'-new',sysdate,l_user,l_type,'','');
end if;
--刪除
if deleting then
   l_col :='del';
   insert into log_file values('bmd_file',:old.bmd01,:old.bmd02,:old.bmd03,:old.bmd04,:old.bmd08,l_col,sysdate,l_user,l_type,'','');
end if;
end;

要刪除 TRIGGER ,就用 drop trigger 名稱,就可以刪除了。

2013年3月25日

更便利的 DIALOG - 同時單頭單身查詢/輸入

剛開始使用 TIPTOP 很多人都會問為什麼查詢要按二次確定,
需要單頭按確認後再到單身再按確認,才會執行查詢的動作,
用 DIALOG 就可以同時做單頭、單身的查詢。

再來操作上不方便的就是,像工單發料單 asfi510 或多主件工程變異單 abmi710 ,
像這種多單身的樣式,就不能同時將二個單身做 scroll bar 的捲動和資料不能複製到剪貼簿,
一般都會再做一個按鈕查詢另一個單身,然後操作完後要再跳回去原本的單身,實在是很不方便。
就是可以用 DIALOG 來達到比較人性化的介面。

在 Genero 官方文件說明:
The DIALOG block is an interactive instruction that executes multiple kinds of sub-controllers simultaneously to drive different parts of a form.

只要將 CONSTRUCT 和 CONSTRUCT ARRAY 都包在 DIALOG 裡面,就可以做到單頭和單身同時查詢。
將二個不同的 DISPLAY ARRAY 也包在 DIALOG 裡面,也一樣可以同時操作這二個單身的 Action 和 Scroll Bar。

DIALOG ATTRIBUTES(UNBUFFERED)
     DISPLAY ARRAY g_bmz to s_bmz.* ATTRIBUTE(COUNT=g_rec_b)
     END DISPLAY
     DISPLAY ARRAY g_bmy to s_bmy.* ATTRIBUTE(COUNT=g_rec_d)
     END DISPLAY
END DIALOG

DIALOG 提供 INPUT、CONSTRUCT、DISPLAY ARRAY、INPUT ARRAY 的多重控制功能,
利用這個功能就可以做到單頭、單身輸入完資料後,才會產生單號和同時寫入資料到 TABLE,
作廢的單子或許就可以少了一些。

要判斷目前 DIALOG 目前控制在那一個的話,提供一個函式 DIALOG.getCurrentItem() 回傳就是畫面的變數值。
這樣多單身就匯出 Excel 就可以判斷要匯出是那一個單身的資料。

官方網站提供其中一個例子做動態的資料查詢,單頭輸入條件後單身就馬上顯示出資料,
原本是寫在 AFTER FEILD 後加上 DISPLAY ARRAY 也是可以做到,但是缺點就是資料的捲軸沒辦法動。
用 DIALOG 就可以做到像 Windows 應用程式類似的作業,盡可能用 DIALOG 來開發程式吧!!

2013年3月15日

回復 Oracle 刪除或是已更新的資料

我們都知道還原資料最多就是只能還原到最近一前的備份點,
但是備份不可能是隨時隨地都在備份,
Oracle 有一個 undo 的機制,會將資料進行異動之前就進行 Data Image,
原本的用意為 A 進行 Query 之後 B 又進行 Update 時,A 的 Query 結果會是當時下達指令時點的資料,
就不會被 B 在 Update 時所影響,也不會造成資料不一致。
因此我們就可以利用此功能回復到你所想要的某一時間的資料。
undo 區分為 ACTIVE、UNEXPIRED、EXPIRED 三種,
當資料 commit 或 rollback 後就會改變為非活動中的狀態。

查看目前 undo 的狀態:
select * from DBA_UNDO_EXTENTS

利用 Oracle 的 undo log 來查到底是誰把資料做刪除或變更的,
使用 LogMiner 來開啟 undo log 或 archived log 檔案,就可以查出 SQL_TEXT 和 Session ,
就知道到底是誰幹的好事。

先來看 Oracle 的參數:
sqlplus: show parameter undo;

select * from v$parameter where name like '%undo%'

NAME                   VALUE
--------------------------
undo_management  AUTO
undo_tablespace     UNDOTBS1
undo_retention        900

undo_retention 保存時間(秒) 900 秒,
設定 undo_retention 越大就會佔用更多的 undo tablespace 的空間。
要是 undo 空間不足時,才會把 undo_retention 以前的資料蓋掉,
當 undo tablespace 設定 RETENTION GUARANTEE 時,
還是不夠 undo 空間就會自動擴展,增加 undo tablespace 檔案容量,
必須要保証磁碟空間是足夠的,當有大量資料異動時,會造成 undo 檔案過大。
設定為 RETENTION NOGUARANTEE , undo tablespace 不會擴增,
當容量不足時就會一直把非活動的空間蓋掉,所以 undo_retention 不保証一定會保存完整資料。

將 undo_retention 改為 1 小時,指令如下:
alter system set undo_retention = 3600

修改 undo tablespace 的屬性,開啟 retention 自動擴展:
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE

關閉 retention 自動擴展:
ALTER TABLESPACE undotbs1 RETENTION NOGUARANTEE

預設 undo_management 都是 auto 管理,
undo_retention 的值會由 Oracle 自動進行最佳的調整,
所以超過 undo_retention 的時間,也有可能狀態還是在 UNEXPIRED 。
因為 Oracle 每 10 分鐘就會依 undo tablespace 的空間和 Oracle 操作狀況來設定最佳的 undo_retention 值。

查看 Oracle 所設定的 undo_retention (秒)
SELECT * FROM v$undostat;

要如何將資料還原,只要加上 timestamp 就可以查到當時的時間點上資料內容。
select * from ds.zx_file as of timestamp to_timestamp('2013-03-15 15:55:29', 'yyyy-mm-dd hh24:mi:ss');

查出來結果應該會很開心,好險資料還在。
再來就是把資料還原就可以了,一般都是用 create table 的方式,再比對資料後做更新。
create table ds.zx_temp as
select * from ds.zx_file as of timestamp to_timestamp('2013-03-15 15:55:29', 'yyyy-mm-dd hh24:mi:ss');

scn (System Change Number) 當資料異動時,就會將 scn + 1 保存在 undo log 。
比較建議還是用 scn 會比較避免同一秒的資料異動時,資料還原的完整性。
再來就是 scn 和 timestamp 在 9i 會有 5 分鐘的資料同步,
所以 9i 用 timestamp 進行 flashback query 時會查不到 5 分鐘內異動的資料。
在 10g 以後的版本就修正此情況, scn 和 timestamp 會在 LogMiner 取得。

改用 scn 的方式來查詢:
select * from ds.zx_file as of scn 2267300000

查詢最近一次資料異動的 scn 和 timestamp :
select scn_to_timestamp(dbms_flashback.get_system_change_number) ,dbms_flashback.get_system_change_number from dual;

雖然 Oracle 這個機制可以讓我們放心資料異動的問題,但還是有限制:
1. Table Schema 變更就不能回復,因 flashback query 是用目前的資料字典。
2. 不能 flashback 到 5 天前的資料。
3. 不能保証 undo_retention 所保存資料是否完整,undo tablespace 過小會蓋掉舊的。
4. drop、truncate 不需 commit ,所以就不能回復,所以要把 truncate 和 drop 指令鎖住。

一般的帳號也可以使用 flashback 的功能,設定權限:
grant execute on dbms_flashback to USER ;

想要看某筆資料所有異動的紀錄,在 Oracle 有 versions 的指令來查看每個版本的資料和起始、結束時間
欄位 versions_starttime 表示版本開始時間,欄位 versions_endtime  表示版本結束時間
使用方式只要在 table 後面加上 versions between timestamp minvalue and maxvalue 就可以了。
範例:select zx_file.*,versions_starttime,versions_endtime from ds.zx_file versions between timestamp minvalue and maxvalue

2013年2月22日

設定報表排程自動發送 Mail 給相關人員

應該是從 TIPTOP 5.2 版之後就有 CR 發送 mail 的功能,
這樣就可以讓系統將異常檢查報表每天都寄給相關人員,
IT 人員也就輕鬆很多了。

系統的運作是:
TIPTOP 主機執行背景程式 → 呼叫 CR 主機報表 → 執行結果存在 CR 主機的 temp 目錄 →
CR 主機將 temp 目錄的檔案發送 mail 。

執行報表後會有約 5 分鐘延遲時間。
執行過報表也都會存在 CR 主機的 temp 目錄,看要不要固定時間清理一下。
因 CR 報表 IIS 為匿名的驗証方式,所以必需將 temp 的目錄改為 everyone 讀取。

首先要先設定 aooi998 Mail 收件人維護作業,
也可以選擇要寄送的是 PDF、Word、Excel …等。

再來就是在 p_cron 建立排程,
通常報表程式的參數五 g_prtway 表示報表的執行結果的顯示方式,
參數四表示是背景作業。
只要參數五設定為 A ,然後背景作業設定為 Y ,
這樣就會自動寄報表的到相關人員的 E-mail 了。

或是只要加上三個變數也可以有寄送 Mail 的功能。
CALL FGL_SETENV("MAIL_TO",g_receiver)
CALL FGL_SETENV("MAIL_CC",g_cc)
CALL FGL_SETENV("MAIL_BCC",g_bcc)
變數的格式為:
收件者名稱:收件者 E-Mail:格式(1.PDF,2.RPT,3.DOC,4.XLS,5.XLS,6.XLS)

2013年2月6日

畫面能夠依資料內容來變更文字顏色、樣式

TIPTOP的畫面設計比較偏向 TEXT-Mode ,所以會以比較單調樣式來呈現,
現在應該沒有人在用這種方式了吧,都是用 GUI 的方式來使用。
這樣就可以添加一些色彩、底線、粗體,來增加使用者操作上資料的辦識能力。

依 4js 的說明,在 DISPLAY 可以加上屬性,如下:
1. BLACK, BLUE, CYAN, GREEN, MAGENTA, RED, WHITE, YELLOW – 顯示顏色
2. BOLD, DIM, NORMAL – 顯示字體樣式
3. REVERSE, BLINK, UNDERLINE – 顯示文字的屬性

屬性的字面上的意思就能知道用途了,多重屬性時只要加上逗號來區隔,就不多加說明了。

例如我們想要 sfb01 這個欄位在某些情況下顯示藍色時,只要加判斷再加上 ATTRIBUTE,
例:
IF g_sfb01.sfb04 = '8' THEN
    DISPLAY BY NAME g_sfb01.sfb01 ATTRIBUTE(BLUE)
ELSE
    DISPLAY BY NAME g_sfb01.sfb01 ATTRIBUTE(BLACK)
END IF

鼎新也有提供 lib 的函式來改變顏色:
CALL cl_set_comp_font_color("sfb01,sfb02","RED")

但是如果是單身的話會用 DISPLAY ARRAY 就只能所有單身全部的顏色都變一樣,
要改用 DIALOG.setCellAttributes 的方式來顯示欄位的屬性。
例:
要先定義單身有那些是要動態變更欄位屬性的陣列變數:
DEFINE g_gen_attr DYNAMIC ARRAY OF RECORD
        gen01       STRING,
        gen02       STRING,
        gen03       STRING
END RECORD

記得要先做 g_gen_attr.clear()。

在 fill() 函數時 FOREACH 的時候判斷:
IF g_gen[g_cnt].gen03 IS NULL TEHN
    LET g_gen_attr[g_cnt].gen01 = 'MAGENTA REVERSE'
ELSE
    LET g_gen_attr[g_cnt].gen01 = ''
END IF

再來就是 DISPLAY ARRAY 顯示的時候依定義好的屬性來顯示:
BEFORE DISPLAY
         CALL cl_navigator_setting( g_curs_index, g_row_count )
         CALL DIALOG.setCellAttributes(g_gen_attr)    #加上這一句

這樣單身也可以依資料來變化顏色了。