2014年12月21日

刪除/縮小 TEMP Tablespace 的檔案大小

Oracle 資料庫必定會有一個 TEMPORARY 的 Tablespace ,主要做為建立暫存資料的時候使用,
有時候建立臨時表的資料量過大,就會不斷的增加 TEMPORARY 的 Tablespace 的檔案容量,
增加後就無法縮小了,就必須要刪除然後再重新建立。

在 TIPTOPGP 5.x ERP 的 4GL 程式中,CREATE TEMP TABLE xxxx AS ....,
並不是在 Oracle 資料庫的 TEMPORARY 建立臨時表,而是在 TIPTOP 的 TEMPTABS 的 Tablespace 建立。
主要原因是 Oracle 的 TEMPORARY 的 Tablespace 只能同一個 Session 才能存取的到,
所以 TIPTOP 是用一般的 Tablespace 的方式來建立,避免 Debug 或呼叫其他程式執行時,就找不到 Temp Table 的情況。

每一個 Tablesapce 是由一個或數個 Datafile 所組成的,像 DBS1 的 Tablespace 不足時就要手動增加 Datafile 讓 DBS1 的 Tablespace 加大。

再來就是如何減少 TEMPORARY 的 Tablespace 了,只能先建立新的 TEMPORARY 再轉移再刪除舊檔的方式。

建立新的 TEMPORARY Tablespace,檔案大小改為 100M,並自動增長每次加 640K。
CREATE TEMPORARY TABLESPACE TEMP1 TEMPFILE
  '/u2/oracle/oradata/topprod/temp01.dbf' SIZE 100M AUTOEXTEND ON NEXT 640K MAXSIZE UNLIMITED
TABLESPACE GROUP ''
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M;

查詢目前資料庫有幾個 TEMPORARY 。
select * from dba_temp_free_space;

查詢目前資料的的主要 TEMPORARY。
select DEFAULT_TABLESPACE, TEMPORARY_TABLESPACE from dba_users where username='SYS';

切換 TEMPORARY 到名稱為 temp1 的 Tablespace。
alter database default temporary tablespace temp1

刪除舊名稱為 temp 的 TEMPORARY Tablespace 的 Datafile 所有檔案。
drop tablespace temp including contents and datafiles;

如果刪除的時候 Hang 住的話,表示 TEMPORARY 正在使用中,查詢正在使用的狀況。
SELECT b.tablespace,b.segfile#,b.segblk#,b.blocks,a.sid,a.serial#,
a.username,a.osuser, a.status
FROM v$session a,v$sort_usage b
WHERE a.saddr = b.session_addr;

如果要減少的話,就是再增加原本的名稱為 TEMP 後,再切換回原本的 TEMP,再刪除TEMP1 的方式。

這樣硬碟可使用空間就變大了。

當然如果覺的 TIPTOP 的 TEMPTABS Tablespace 過大的話,也是可以刪除再重新建立,
有時候跑一些比較大的程式如 axcp120、axcp500…等,資料異常有時候會造成無限圈的情況。就會不斷的增加 TEMPTABS 的 Datafile 大小。

重建 TEMPTABS 的 Tablespace。
DROP TABLESPACE TEMPTABS INCLUDING CONTENTS AND DATAFILES;

CREATE TABLESPACE TEMPTABS DATAFILE
  '/u2/oracle/oradata/topprod/temptabs.dbf' SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED
LOGGING
ONLINE
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT AUTO
FLASHBACK ON;

2014年9月5日

TIPTOP 單身資料在 EFGP 寄送 mail 時也可以顯示出來

EasyFlowGP 要寄送通知或簽核的 E-mail 時,單身的資料通常是不能顯示在 E-mail 上。
這時候只好先把單身的資料都暫存到一個單頭的欄位上,再傳到 EFGP 的隱藏欄位中。
但是要怎樣才能做出單身的樣式呢?
因為 Easyflow 發信的格式是 HTML ,就可以加上 HTML 在附加在 mail 上就可以了。

方法:
1. 修改 4gl 程式將單身的資料改合併到一個大的字串欄位的。 (通常我都會用自定義欄位改為 varchar(3000)
    LET g_czaa.czaa06 = '<tr><td>員工<td>姓名
    FOR l_n = 1 TO g_cza.getlength()
           LET g_czaa.czaa06 = g_czaa.czaa06,<tr><td>,g_cza[l_n].cza01,'<td>',g_cza[l_n].cpf02,'
    END FOR
2. 將此欄位加到 aws_efcfg2 傳送到 EFGP 。
3. EFGP 的表單增加一個隱藏欄位。
4. EFGP 的流程中帶出此表單的欄位。
    <center><table width=98% border=1><#apyt103~~czaa06></table></center>
5. 完成。

要注意的是因為是用 XML 傳送欄位的資料到 EFGP 上,所以 HTML 就不寫上結尾的語句,
不能寫上 </td>、</tr> 不然就會欄位的資料是空白的。
當然也有另外的方法可以做,就是另外寫一支 ASP.NET 的程式利用 HTML 嵌入動態的網頁到 mail 裡。

再來就是測試看看啦。



2014年8月15日

將資料透過 COM 的方式連結到 Outlook 新郵件中

有時候資料輸入到系統中,想要 E-mail 給其他人員確認某些資料時,
希望能夠在 Oulook 的新郵件中預設帶出帶出剛剛輸入的資料,就不需要重複再輸入一次。
通常應用程式相互溝通串連時,都有提供 COM 的方式連結,
因此我們就可以利用這個功能來自動產生新的郵件。
可以參考之前的文章使用 COM 方式實現 Excel 資料匯入匯出的功能

只要用文字變數,再將內文轉換成 HTML 的語法,就可以實現將 TIPTOP 的資料帶到 Outlook 新增郵件中。
這樣使用者只要修改一下內容,或是修改要寄送的人員,再發送郵件就可以了。

另外的方式就是想要寄送郵件都是由使用者的 Outlook 來寄送的話,不是透過 TIPTOP 的 SMTP 的方式,
另方面可以讓使用者的信箱有寄件備份的記錄,
可以做到背景產生新郵件,並且自動發送就會有寄件備份的紀錄了。
也可以做到產生連絡人或是行事曆、會議等功能,算是另一項系統整合的方法了吧。

說明一下 COM 的指令如下用法:
1. CreateInstance:The CreateInstance function creates an instance of a registered COM object.
2. CallMethod:The CallMethod function calls a method on a specified object.
3. GetProperty:The GetProperty function gets a property of an object.
4. SetProperty:The SetProperty function sets a property of an object.
5. GetError:The GetError function gets a description of the last error which occurred.
6. ReleaseInstance:The ReleaseInstance function releases an Instance of a COM object.

再套到 Outlook 開啟新郵件的語法如下:
// 新增連結
   CALL ui.interface.frontcall("WinCOM","CreateInstance",["Outlook.Application"], [outapp])
// 開啟新的郵件
   CALL ui.interface.frontcall("WinCOM","CallMethod",[outapp, "CreateItem(olMailItem)"], [outit])
// 開啟畫面
   CALL ui.interface.frontCall("WinCOM","CallMethod",[outit, "Display"], [outcon])
// 郵件的內容
   CALL ui.interface.frontCall("WinCOM","SetProperty",[outit, "HTMLBody","<HTML><BODY>郵件內容</BODY></HTML>"], [result])
// 郵件主旨
   CALL ui.interface.frontCall("WinCOM","SetProperty",[outit, "Subject","郵件主旨"], [result])
// 副本收件者
   CALL ui.interface.frontCall("WinCOM","SetProperty",[outit, "cc","張世勳 4shiun@gmail.com"], [result])
// 收件者
   CALL ui.interface.frontCall("WinCOM","SetProperty",[outit, "to","張世勳 4shiun@gmail.com"], [result])
// 密件副本收件者
   CALL ui.interface.frontCall("WinCOM","SetProperty",[outit, "bcc","張世勳 4shiun@gmail.com"], [result])
// 傳送郵件
   CALL ui.interface.frontCall("WinCOM","CallMethod",[outit, "Send"], [outcon])
// 關閉連結
   CALL ui.interface.frontcall("WinCOM","ReleaseInstance",[outapp],[result])

2014年8月6日

如何直覺式知道 axmi221 其他地址和客戶聯絡人有沒有內容 ?

過去的文章可以用一個圖示就知道有沒有附件的資料-增加相關文件圖示
方便查詢時就不需要每個都按相關文件去查看。

那如果是 Action 呢? 能不能知道 Action 串連的程式有沒有資料。
舉例: axmi221 客戶基本資料能不能直覺式就看出來有沒有其他地址和客戶聯絡人呢?

其實也是可以的,在 Action 可以動態加上 image 的圖示就可以了,
沒辦法改變 Action 的文字顏色或是背景的顏色。
Genero 已經有預設一些圖示提供給 TIPTOP 可以直接宣告來使用,
想要看有那些圖示,請參考:/u1/genero/gas/pic/ 資料夾的 png 檔。



再來就是依範例加上此段程式就可以啦。有資料的時候就會顯示圖示,沒有資料的話就不會顯示圖示。
這樣就方便使用者可以很直覺的知道有沒有輸入資料了。

     # 有客户其他地址時顯示符號
     LET l_cnt = 0
     SELECT COUNT(*) INTO l_cnt FROM ocd_file WHERE ocd01 = g_occ.occ01
     IF l_cnt > 0 THEN
        LET w = ui.Window.getCurrent()
        LET n = w.findNode("MenuAction","customer_address1")
        CALL n.setAttribute("image","information")
     ELSE
        LET w = ui.Window.getCurrent()
        LET n = w.findNode("MenuAction","customer_address1")
        CALL n.setAttribute("image","")
     END IF
    
     # 有客户聯絡人時顯示符號
     LET l_cnt = 0
     SELECT COUNT(*) INTO l_cnt FROM oce_file WHERE oce01 = g_occ.occ01
     IF l_cnt > 0 THEN
        LET w = ui.Window.getCurrent()
        LET n = w.findNode("MenuAction","customer_contact")
        CALL n.setAttribute("image","information")
     ELSE
        LET w = ui.Window.getCurrent()
        LET n = w.findNode("MenuAction","customer_contact")
        CALL n.setAttribute("image","")
     END IF

2014年7月26日

把鼎新 WorkFlow 7.x 也一起虛擬化

會買鼎新 TIPTOP GP,我猜有 9 成是從 Workflow 升級吧,
然而從 Workflow 升級到 TIPTOP GP,大多都會把原本在 Workflow 上的營運中心的都轉換到 TIPTOP上。
這樣 Workflow 就不太需要報表、AP、DB 都分開,可以全部併成同一台虛擬機器上,
實體機器就可以拿去別的用途。

1.首先要記得原本序號的 MAC Address,新增一台虛擬機一樣輸入上此 MAC Address。
2.再來就是安裝 SQL Server 2005 和 Service Pack 4。
3.安裝 Workflow 7.x 的 AP、DB、報表的程式。
4.關閉系統控制管理員 Systemcontrols、Borland Socket Server Fix。
5.將原本在 Workflow 主機上的 SQL Server 資料庫完整的備份下來。(每天都有備份吧,用備份檔也可以)
6.用備份檔還原資料庫,將備份檔 copy 到虛擬機的 C:\ ,再依序的還原所有的資料庫,也可以下指令方式還原:
 RESTORE DATABASE [DSCRPT] FROM DISK = N'C:\DSRPT.bak' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 10
 GO
7.再還原 Conductor 、RTB 資料夾,將備份檔整個目錄覆蓋就可以了。
8.如果有合併 AP、DB、報表主機的話,就要修改 ConductorC.INI 和 ConductorS.INI 檔。
9.再來就是註冊 Workflow ,將原本的 WfErp.ini 複製到虛擬機上。
10.執行系統控制管理員 Systemcontrols ,就會出現"就是這個"的按鈕,進行註冊。
11.輸入序號,原本的主機上的系統控制管理員 Systemcontrols 上面有序號。
12.最後 RTB 資料夾和 Conductor 開啟網路磁碟分享和設定權限。

記得要安裝一台印表機,並設定紙張格式、大小。還要記得開啟派班中心 Dispatcher。
這樣就把 Workflow 也虛擬化了,以後備份或是災難復原就簡單多了,只要找一台主機掛上虛擬機就可以了。

2014年7月16日

將 EFGP 也虛擬化吧

把 Crystal Report 主機虛擬化後,
也把 TIPTOP 主機也虛擬化,
再來就是將 EasyFlow GP (EFGP) 也虛擬化吧。
這樣就完整的一套虛擬化了,以後還可以考慮放上 Cloud 上。

可以利用 Hyper-V 2012 R2 的複寫的功能,就可以做到備援和備份的機制。
以後就不用擔心硬體主機故障了,
也隨時可以轉移到另一台主機,方便上班時間可以做維護,就不用等到下班才能做了 (不能報加班)。

由於 EFGP 虛擬化時,需要額外再購買 GuardService 主機才能正常的使用。
不然只有 Administrator 帳號才能登入而已。
因為每次帳號要登入的時候都會去 Check 一次 Guard Service ,
但是 Guard Service 也是一台硬體主機,也會有故障的一天,
萬一 Guard Service 主機掛了也是只能請鼎新來報修,重新裝機後 EFGP才能正常使用 。
(個人覺得 Guard Service 用在一般 PC上,反而比伺服器主機更早掛)
而且 Guard Servicer 是不能放在虛擬主機上的。
真搞不懂這一台 Guard Service 的用意在那??
奇怪,怎麼不出一個不卡 Guard Service 的版本就好了,又不是每一家公司都需要這一個機制。

EFGP 虛擬化後就出現這個頁籤。


使用者登入會出現這個視窗。



2014年1月11日

匯率從 2014 開始台灣銀行抓不到的問題

台灣銀行自從 2014/1/1 開始,網頁內容就有做一些變更,
造成 TIPTOP 判斷網頁內容錯誤,無法抓到指定的 HTML 內容來抓取匯率的下載網址。

分析一下 4GL 的程式,是副程式的 s_exrate.4gl 抓取,
透過 exrate.jar 的 JAVA 程式把網頁下載到 /reout 的目錄下。
因此就要從 exrate.jar 去下手,修改正確的網址連結。

首先要先反組譯,將藍色的部份做修改,然後再重新組譯再製成 JAR 檔就大功告成了,
以下是修改的 JAVA 程式碼:

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 來設定,日後比較方便維護和紀錄。