วันพฤหัสบดีที่ 28 กุมภาพันธ์ พ.ศ. 2556

RPG เรียกใช้ SQL cmd

RPG เรียกใช้ SQL cmd


น.ศ. ที่จบใหม่  จะใช้ SQL statement คล่อง  เช่น  ตรวจสอบ Data ด้วย SQL แล้วนำ SQL ดังกล่าวมาเขียนโปรแกรม
แต่วิธีนี้  ใช้กับ  RPG ทั่วไปไม่ได้  (ต้องติดต่อ ทีละ File)

 ในขณะที่ IBM ได้พัฒนาการทำงานใน SQL ทำงานได้เร็วกว่า การเขียนแบบปรกติ (ฟังจากสัมนาของ IBM)    
Q:  “IBM ไม่เปิดช่องให้เขียน RPG ร่วมกับ SQL ?” 
A: ทำได้ครับ


ต้องเขียนใน  QRPGLESRC   กำหนดชนิดของ Source เป็น SQLRPGLE
                           Work with Members Using PDM                 A39BP  
File  . . . . . .   QRPGLESRC             
   Library . . . .     QGPL                  Position to  . . . . .            
Type options, press Enter.                                                   
  2=Edit         3=Copy  4=Delete 5=Display       6=Print     7=Rename        
  8=Display description  9=Save  13=Change text  14=Compile  15=Create module..
 Opt  Member      Type        Text                                            
      MYPGM       SQLRPGLE    REPORT
              

โครงสร้างการเขียน (แบบย่อ)
  • เตรียม SQL statement  (ถ้า Run จะได้  Data Set)Select ได้ DataSet ออกมา -> จัดการทีละ row เรียก Cursor  (update, insert, …)
  • กำหนด ต้วแปร  ชนิด Cursor  (เช่น Cur0)  ให้เรียกใช้ SQL statement(ถ้า SQL statement ยาวไป แยกเขียนได้)
  • Open  ตัวแปรชนิด Cursor (Cur0)
  •  กำหนด  ถ้าไม่มีข้อมูล ให้ไปตำแหน่ง Tag ที่ Close
  •  Loop   จนกว่า  End of File   (SQLCOD = 0)
    • (Fetch) อ่านทีละ row เข้าตัวแปร
    • นำตัวแปร  ไปใช้งาน เช่น สั่งพิมพ์
  • Tag : Close
  • Close ตัวแปรชนิด Cursor (Cur0)

ตย.โปรแกรม  อ่านข้อมูล แล้วสั่งพิมพ์


FQPRINT    O    F  132        PRINTER OFLIND(*INOF)              
D*                                                               
D strSQL          S          10000A   VARYING  INZ               
D*                                                               
D dsField0        Ds                                             
D  E4PRSC                 1      5                               
D  NAME                   6     15                               
C* ----------------------------------------------------------- **
C     *ENTRY        PLIST                                        
C                   PARM                    Plac              2  
C* ----------------------------------------------------------- **
 /FREE                                                           
   strSQL =                 ' SELECT E4PRSC,E4PABN FROM          
   strSQL = %TRIM(strSQL) + ' TPPR1D/PE040P                      
   strSQL = %TRIM(strSQL) + ' WHERE E4PLAC = '''+PLAC+'''         
   strSQL = %TRIM(strSQL) +   ' AND E4PRSC <= ''AZ  ''            
 /END-FREE                                                        
C* ----------------------------------------------------------- ** 
C/EXEC SQL                                                        
C+ PREPARE MAINSELECT FROM :strSQL                                
C/END-EXEC                                                        
C*                                                                
C/EXEC SQL                                                        
C+ DECLARE CUR0 CURSOR FOR MAINSELECT                               
C/END-EXEC                                                        
C*
C/EXEC SQL                                                        
C+ OPEN CUR0                                                        
C/END-EXEC                                                        
C*                                                                
C/EXEC SQL                                                        
C+ WHENEVER NOT FOUND GO TO CLOSE_C0                              
C/END-EXEC                                                        
C*                                                                
C                   z-add     0             CNT#              4 0 
C                   EXCEPT    HED010                              
C*                  -----------------------Loop Until no Data     
C                   Dow       SQLCOD = 0                          
C                   EXSR      FetchC0                             
**                            --If EOF goto CLOSR_C0              
C                   EVAL      CNT# = CNT#+1                       
C   OF              EXCEPT    HED010                              
C                   EXCEPT    DTL010                              
C                   ENDDO                                         
**                                                                
C     CLOSE_C0      TAG                                           
**    -----------------                                            
C/EXEC SQL                                                         
C+ CLOSE CUR0                                                        
C/END-EXEC                                                         
C*                  ---SQL:no data---                              
C                   IF        CNT# ='0'                            
C                   EXCEPT    ERR010                               
C                   ENDIF                                          
C*                                                                 
C                   SETON                                        LR
C   LR              RETURN                                         
**----------------------------------------------------------- **   
C     FetchC0       BEGSR                                          
C*-----------------------     
C*                  -Use DS for read Var (rename)                      
C/EXEC SQL                                                         
C+ FETCH NEXT FROM CUR0 INTO :dsField0                               
C/END-EXEC                                                         
C                   ENDSR                                         
** ----------------------------------------------------------- ** 
OQPRINT    E            HED010         1  2                       
O                       plac                 2                    
O                                           30 '*** TEST SQL ***' 
O          E            DTL010         1                          
O                       E4PRSC              10                    
O                       NAME                22                    
O          E            ERR010         1                 
O                                           10 '-NO DATA-'

ทดสอบโดย   

Call    MYPGM   (‘T2’)  ส่งค่าที่ทำให้มี Data กับ ไม่มี Data

อธิบาย Code

D strSQL          S          10000A   VARYING  INZ               
ประกาศตัวแปร  strSQL  เพื่อป้อน SQL Statement
D dsField0        Ds                                             
D  E4PRSC                 1      5                               
D  NAME                   6     15                               
กำหนด DS เพื่อรับค่า  Field จาก SQL
C     *ENTRY        PLIST                                        
C                   PARM                    Plac              2  
RPG รับค่าเข้ามา  (ทดสอบ แบบมี Data หรือ ไม่มี Data)
/FREE                                                           
   strSQL =                 ' SELECT E4PRSC,E4PABN FROM          
   strSQL = %TRIM(strSQL) + ' TPPR1D/PE040P                      
   strSQL = %TRIM(strSQL) + ' WHERE E4PLAC = '''+PLAC+'''         
   strSQL = %TRIM(strSQL) +   ' AND E4PRSC <= ''AZ  ''            
 /END-FREE                                                       
เขียนแบบ Free From    ''' จะได้ ' 2 ตัว 

C/EXEC SQL                                                        
C+ PREPARE MAINSELECT FROM :strSQL                                
C/END-EXEC                                                        
C*                                                                
C/EXEC SQL                                                        
C+ DECLARE C0 CURSOR FOR MAINSELECT                               
C/END-EXEC                                                         
ใช้ /EXEC SQL  … .END-EXEC   เพื่อเรียกใช้ SQL Statement  (1 block  ต่อ 1 คำสั่ง)
(เขียนแบบนี้รู้สึกว่า  เปลืองบรรทัดยังไง ไม่รู้)
กำหนด ต้วแปร  ชนิด Cursor  (เช่น Cur0)  ให้เรียกใช้ SQL statement

C/EXEC SQL                                                        
C+ OPEN C0                                                        
C/END-EXEC                                                        
Open  ตัวแปรชนิด Cursor

C/EXEC SQL                                                        
C+ WHENEVER NOT FOUND GO TO CLOSE_C0                              
C/END-EXEC                                                        
กำหนด  ถ้าไม่มีข้อมูล ให้ไปตำแหน่ง Tag ชื่อ Close_C0

C                   z-add     0             CNT#              4 0 
C                   EXCEPT    HED010                              
C*                  -----------------------Loop Until no Data     
C                   Dow       SQLCOD = 0                          
C                   EXSR      FetchC0                             
**                            --If EOF goto CLOSR_C0              
(Fetch) อ่านข้อมูล  ถ้าไม่มีข้อมูลจะ  กระโดดไปตำแหน่ง Tag ชื่อ Close_C0
แยก Fetch ออกเป็น  Sub-Routine (ทำให้ Code อ่านง่าย)


C                   EVAL      CNT# = CNT#+1                       
C   OF              EXCEPT    HED010                              
C                   EXCEPT    DTL010                              
C                   ENDDO                                         
**                                                                
C     CLOSE_C0      TAG                                           
**    -----------------                                            
Tag ที่รับเมื่อ  ไม่มีข้อมูล,ข้อมูลหมด

C/EXEC SQL                                                         
C+ CLOSE C0                                                        
C/END-EXEC                                                         
Close (ใช้คู่กับ Open)

C*                  ---SQL:no data---                              
C                   IF        CNT# ='0'                            
C                   EXCEPT    ERR010                               
C                   ENDIF                                          
C*                                                                 
C                   SETON                                        LR
C   LR              RETURN                                         
**----------------------------------------------------------- **   
C     FetchC0       BEGSR                                          
C*-----------------------     
C*                  -Use DS for read Var (rename)                      
C/EXEC SQL                                                         
C+ FETCH NEXT FROM C0 INTO :dsField0                               
C/END-EXEC                                                         
C                   ENDSR                                          
แยก Fetch ออกเป็น  Sub-Routine (ทำให้ Code อ่านง่าย) 

ดู Complier Listing

จะเห็นว่า  Compiler เพิ่ม  "ตัวแปร" ที่เกี่ยวข้องกับ SQL  (มีหลายตัว)
และเปลี่ยน /Exec SQL เป็นชุดคำสั่ง RPG  

ลอง Debug ดู

ใช้ StrDbg cmd น๊ะครับ (ใช้ StrISDB ไม่ได้)
ผู้อ่านที่เขียน  Style แบบนี้ 
KEY   SETLL  File
KEY   READE  FILE     80
      ---init before Loop---
*IN80 DOWEQ '0'
      ---process---
KEY   READE  FILE     80
      ENDDO                                                  

จะพบว่า  Code ข้างต้น  ถ้า Fetch เทียบเท่ากับ Read  ตำแหน่งการวางต้องมี 2 บรรทัด
ในความจริง    Fetch จะทำงาน เหมือน Try ... Catch  ทำงานร่วมกับ WHEN EVER (ด้านบน)
- อ่านแล้วพบว่า End Of File จะต้องกระโดดไปที่  CLOSE_C0

ดังนั้น เมื่อ End Of File จะไม่ทำคำสั่งนี้
C                   EVAL      CNT# = CNT#+1     

สรุป

สำหรับโปรแกรมง่ายๆ  Code แบบวิธี RPG ดั้งเดิม  จะเขียนได้สั้นกว่า
แต่ถ้า  ใช้งานทีละหลาย File (ที่มี Relation)   การเขียนแบบนี้จะช่วยได้มาก
(และ สามารถนำไปทดสอบกับ SQL Editor ภายนอกก่อนได้)

วิเคราะห์

ถ้ามันดี  แล้วทำไม  ผมถึงไม่ใช้งาน ?
(ผมใช้ SQL ตรวจข้อมูลได้เร็วกว่า เขียนโปรแกรม)

ในช่วง 20 ปี ของการใช้ RPG มีการสร้าง Tool หลายตัวเข้ามาช่วยทำงาน
Tool ดังกล่าว  ไม่สามารถใช้กับ  Code ที่เขียนร่วมกับ SQL ได้  เช่น
  • Tool ที่ใช้ตรวจ  program ในระบบฯ มีใช้ File อะไรบ้าง ? (DSPPGMREF cmd)
    (บริษัทฯ มีใช้ Tool ตัวนี้)
  • STRISDB  cmd 
  • ปัญหาเรื่องความเคยชิน  ของโปรแกรมเมอร์ ที่ทำงานมานาน (ช้า เมื่อย้ายไปเขียน  วิธีใหม่)

วันอาทิตย์ที่ 24 กุมภาพันธ์ พ.ศ. 2556

Data Area

Data Area


Data Area ทำงานเหมือน File ที่มีเพียง 1 Record  1 Field   ทำให้การเรียกใช้จัดการทำได้ง่าย แต่ไม่สื่อความหมาย (ไม่รู้เลยว่า ข้างในแบ่งเป็น Field ย่อยอะไรบ้าง)

งานที่นิยมใช้ - ข้อมูลขนาดกระทัดรัด

  • จัดเก็บ  User Name (หลัง LogOn ผ่านแล้ว) งานอื่นๆใน Job เดียวกัน (เหมือนตัวแปร Session ของ Web)
  • จัดเก็บ  สถานะ  เช่น  Update ต้องผ่าน 4 ขั้นตอน เช่น  “ABCD”  ณ ตอนนี้ทำถึง  C แล้ว (ควบคุม work flow,approve)
  • จัดเก็บ  Run No เช่น  “00152”   Invoice No เลขที่ล่าสุด คือ หมายเลข 00152
  • งวด/รอบบัญชี  เช่น “2013/02”  วันนี้  วันที่  2013/03/01  แต่ปี/เดือนของบัญชี  ยังเป็น 2013/02 (ยังปิดบัญชีไม่เสร็จ)

มี 2 แบบ คือ  
  1. Local Var ประจำ Job ใน AS/400  (คล้าย  ตัวแปร Session ของ Web) … ถูกสร้างอัตโนมัติ และหายไปเมื่อ
      1. จัดเก็บลง Library  ... ต้องสร้างด้วยคำสั่ง CRTDTAARA

การจัดการด้วย CL Cmd

ตัวอย่าง  การสร้าง Data Area

CRTDTAARA DTAARA(QGPL/CTRLYM)    
         TYPE(*CHAR)        LEN(6)        TEXT('DATE CONTROL YYYY/MM')

CRTDTAARA DTAARA(QGPL/RUNNO)    
         TYPE(*DEC)         LEN(5 0)      TEXT('LAST INVOICE NO')

CRTDTAARA DTAARA(QGPL/UPDSTS)    
         TYPE(*CHAR)        LEN(10)       TEXT('UPDATE STATUS')
CRTDTAARA DTAARA(QGPL/BALQTY)    
         TYPE(*CHAR)        LEN(10)       TEXT('BALANCE QTY')

ตัวอย่าง  คำสั่งการเปลี่ยนค่าใน Data Area

CHGDTAARA DTAARA(QGPL/CTRLYM)
         VALUE(‘201302’)    

ตัวอย่าง  คำสั่งที่ใช้แสดงค่าใน Data Area
DSPDTAARA DTAARA(QGPL/CTRLYM)

ตัวอย่าง  คำสั่งการเปลี่ยนค่าใน Data Area  โดยระบุตำแหน่ง
ต้องการเปลี่ยนเฉพาะ ค่า "เดือน" อยู่ในตำแหน่งที่ 5 ไป 2 อักษร
                        Change Data Area (CHGDTAARA)
Type choices, press Enter.
Data area specification:
 Data area  . . . . . . . . . . > CTRLYM        Name, *LDA, *GDA, *PDA 
   Library  . . . . . . . . . . >   QGPL        Name, *LIBL, *CURLIB
 Substring specifications:
 Substring starting position  . > 5             1-2000, *ALL
 Substring length . . . . . . . > 2            1-2000
New value  . . . . . . . . . . . > '03'


แบบฝึกหัด
1. กำหนด   Data Area  เพื่อจัดเก็บ  ปีเดือน  และ  วันสุดท้ายของเดือนนั้น  
 เช่น  2013/09/30  (มี / คั่นด้วย)

2. สร้างใน Library ของตนเอง
3. บันทึก  และ   ดู   ด้วยคำสั่ง  ข้างต้น

การจัดการด้วย RPG

[สร้าง Data Area
จาก ตย ข้างต้น]

ประกาศ ตัวแปรและให้ดึงจาก Data Area (เป็นชนิดอักษร)
อ่าน   ค่า  ปี,เดือน  (โดย Lock เพื่อป้องกันคนอื่นเรียกใช้)
นำค่า มาแสดงผลบนหน้าจอ  (ให้แก้ไขบนหน้าจอ)
บันทึก  ค่า  ปี,เดือน  ใหม่ที่รับจากหน้าจอ (พร้อมทั้ง  ยกเลิก การ Lock)

C           *NAMVAR   DEFN            CTRLYM  6
C*C*--                  IN   CTRLYM                    NO PROTECT
C           *LOCK     IN   CTRLYM                    LOCKC*
C*C                     MOVELCTRLYM     W1CTYY   
C                     MOVE CTRLYM     W1CTMM
C*
C                     EXFMTDSP01W
C*
C                     OUT  CTRLYM
C                     UNLCKCTRLYM

แบบฝึกหัด (ต่อ)
  1. สร้าง RPG ที่อ่าน Run No (Data Area) เรียก RPG ใน 2 job
  2. เพิ่ม Code เพื่อป้องกัน (Lock) ต้องให้ Run No เสร็จก่อน อีก job จึงจะแสดงผลได้

การประกาศ ในแบบต่างๆ

ประกาศ แล้วใช้ชื่อ ตัวเแปรอื่น
เช่น Data Area = CTRLYM แต่ในโปรแกรมจะใช้ผ่าน W1YM เป็นต้น
C           *NAMVAR   DEFN CTRLYM     W1YM 

ประกาศ หลายค่าและดึงค่าพร้อมๆกัน ในครั้งเดียว
C           *NAMVAR   DEFN            CTRLYM  6
C
*NAMVAR   DEFN            RUNNO  4
C
*NAMVAR   DEFN            BALQTY 102
C*
C           *LOCK     IN   *NAMVAR
C*
C                OUT  *NAMVAR

Local Data Area 

มีลักษณะ  “คล้ายกัน” แต่จะใช้ชื่อ ตัวแปรดังนี้ "LDA" โดยมีขนาด 1,024 Tip Data Area ไม่มีโครงสร้าง   มักจะใช้ Data Structure (กำหนดใน I–Spec) ช่วยในการกำหนดโครงสร้างใน Data Area 

ICTRLYM      DS
I                                        1    8 CTLYMD 
I                                        1    4 CTLY 
I                                        5    6 CTLM
I                                        7    8 CTLD

ILDA        UDS  
I                                        1  15 EMPNO
I                                       16  45 EMPNM 

C           *NAMVAR   DEFN *LDA      LDA   
C*
C                     IN   LDA
C*

แบบฝึกหัด (ต่อ)
  1. RPG-1 กำหนดให้ Local Data Area มีค่า "Say Hi"
  2. ใน Job เดียวกัน RPG-2 ดึงค่า Local Data Area มาแสดงผล (ต้องมีค่าตามข้อ 1) เปิดใช้ต่าง Job เรียกใช้ RPG-2 (Data Area ต้องไม่มีค่า)

วันเสาร์ที่ 26 มกราคม พ.ศ. 2556

ออกแบบ หน้าจอ

ออกแบบ หน้าจอ (Display File)

นักศึกษาที่จบใหม่ ส่วนใหญ่ จะมีปัญหากับการออกแบบหน้าจอ (Display File)
กับชุดโปรแกรม RPG ที่มีอยู่  ...   อืมส์ ก็มันคนละยุคกันนี่ครับ :P

ไม่เป็นไร   แค่เข้าใจและประยุกต์ให้เหมาะสมก็ทำงานด้วยกันได้ง่ายแล้ว   เอาล่ะมาเริ่มกันเลยครับ

ชนิดของหน้าจอ

ผมแบ่ง  การเรียกใช้โปรแกรม RPG  ออกเป็น   กลุ่มดังนี้

a. ไม่ใช้หน้าจอ เลย  ... อันนี้ไม่พูดถึงน๊ะครับ
    ตย. ที่ใช้บ่อย  คือ  เรียกรายงานแบบ Batch
b. หน้าจอ  โดยไม่ติดต่อกับ DB  ...  มักทำเป็นแบบฝึกหัด
c. ใข้หน้าจอ  ติดต่อ DB ทีละ Record
    c.1 ดูอย่างเดียว
    c.2 add, change, delete Record
d. ใช้หน้าจอ  ติดต่อ DB ทีละหลาย Records  ... รู้จักกันในชื่อ Sub-File
    d.1 ดูอย่างเดียว 
    d.2 add, change, delete Record 

ลองดูภาพ   แล้วเทียบกับที่แต่ละคน  ออกแบบมาน๊ะครับ (เหมือน หรือ แตกต่าง)

หน้าจอ  โดยไม่ติดต่อกับ DB

จากภาพ  มีอะไร ?  ที่แตกต่างจากการออกแบบในยุคนี้ครับ ?
- ไม่ใช้  วิธีคลิก ปุ่ม (Submit) แต่ต้องกด Enter ...  แทบจะไม่ใช้งาน Mouse เลย
- การยกเลิกการใช้   ไม่ใช้ปิด Windows  แต่ต้องกดปุ่ม
   (F1 = ใช้ Function Key แถวบนของ Keyboard)

เทคนิคอื่นๆ 
- มุมบนซ้าย  ใส่ code   "w1","w2"  ใช้บอกสถานะว่า  กำลังใช้  หน้าจอลำดับไหนอยู่
- หน้าจอ  มีคำอธิบายที่ "ชัดเจน" (ไม่ต้องทำคุ่มือการใช้)
- ช่องที่ป้อนข้อมูลได้  จะเห็นเป็น "จุด" (Column Seperate) 
  ช่องไหน ที่ป้อนแล้ว  จะแสดงด้วย  "ตีเส้นใต้" (Underline)

Note   RPG พัฒนามาจากเครื่องมินิคอมพ์พิวเตอร์   
         มี Server ส่วนกลางตัวเดียว รับคำสั่งจาก PC (หรือ workstation) ผ่านการกด Enter  
จะเป็นการดีที่ให้ Server ทำงานน้อยเท่าที่จำเป็น  โดยการออกแบบหน้าจอที่เหมาะสม

หน้าจอ  ติดต่อ DB ทีละ Record

แบบดูผลอย่างเดียว


การออกแบบปัจจุบัน มักจะมี Dropdown รายชื่อพนักงานให้เลือก (ต้องเพิ่ม อ่านข้อมูล มาแสดง)
ตัวหน้าจอ  ต้องทำให้ Dropdown ทำงาน
- ถ้าตัวโปรแกรม  ทำงานบน Server  เท่ากับว่า  "เพิ่มภาระ" ให้ Server
- ถ้าตัวโปรแกรม  ทำงานบน PC ตัวมันเองก็จะดี (เช่น Java Script)

แต่ RPG ทำงานบน Server น๊ะ (ไม่ใช่ PC)  แนวคิดแบบข้างต้น ไม่เหมาะ (Server ต้องทำงานมากเกินไป)

แบบ add, change, delete Record

หน้าจอที่ 1 (w1) ป้อน Employee No  กด Enter
โปรแกรม จะตรวจ  
    ถ้า DB ไม่มีข้อมูล  จะเตรียม  หน้าจอที่ 2 (w2)    จะขึ้นสถานะ "Add" (ภาพแถวบน)
         บันทึกข้อมูล  แล้วกด Enter
              แสดง หน้าจอที่ 3 (w3) เพื่อยืนยัน (confirm)
    ถ้า DB มีข้อมูล  จะ Load ข้อมูล  หน้าจอที่ 2 (w2)    จะขึ้นสถานะ "Change" (ภาพแถวล่าง)
         ปรับ/แก้ไข  แล้วกด Enter
              แสดง หน้าจอที่ 3 (w3) เพื่อยืนยัน (confirm)

ถ้าต้องการ  ออกจากโปรแกรม ให้ กด F1
ถ้าต้องการ  ถอยกลับไปหน้าจอ ก่อนหน้า ให้กด F3

สังเกต  หน้าจอมีลักษณะเหมือนกัน    ในการออกแบบเราสามารถใช้ชุดเดียวกันได้

หน้าจอ  ติดต่อ DB ทีละหลาย Record

หน้าจอ ดูอย่างเดียว

รู้จักกันในชื่อ Sub-File

หน้าจอที่ 1 (w1) ป้อน  Employee No  เพื่อกำหนดจุดแสดงเริ่มต้น เช่น เริ่มต้นที่ B6940
เมื่อกด Enter จะแสดง รายการที่เริ่มต้น
(เทียบกับ .Net ก็คือ GridView)

หน้าจอ  add, change, delete Record 


หน้าจอจะคล้ายกัน  แต่ "เพิ่ม"  ช่องให้เลือกรายการข้อมูลได้
เมื่อเลือกแล้ว หน้าจอจะแสดง  หน้าจอติดต่อกับ DB 1 record
(เทียบกับ .Net ก็คือ GridView แบบแสดงตัวเลือก)

มาถึงจุดนี้  คงเห็นภาพขั้นต้น เพื่อที่จะเขียนโปรแกรมได้แล้วน๊ะครับ
ถัดไป  ก็เขียนโปรแกรม Flow

อืมส์... บางคนคงอึดอัดนิดๆ  เพราะ Developer มักจะคิด "ทุกด้่าน" ในครั้งเดียว
แต่ถ้าเรียงตามลำดับ คือ เราต้องคุ้นกับ "ข้อจำกัด" ก่อน แล้วจึงปรับตัวเลือกตาม


วันพฤหัสบดีที่ 10 มกราคม พ.ศ. 2556

ใช้ Editor SEU

ในการเขียน Source Code Editor พื้นฐานคือ  Source Entry Utility (SEU) 
ข้อสังเกต                วิธีใช้งาน SEU  เหมือนโปรแกรม RW (ราชวิถี เวิร์ด),CW (จุฬา เวิร์ด)   ยุคปี คศ. 198x (20 ปีแล้ว)
ปัจจุบัน มี Editor เฉพาะทางเพิ่มขึ้น
 สำหรับ ผู้ที่ต้องทำงาน  ร่วมกับ ทีมงานเก่า  ควรใช้  SEU ได้คล่องระดับหนึ่ง  ก่อนจะเปลี่ยนไปใช้ Editor เฉพาะ

ขั้นตอนการเขียน Code (แบบย่อ)
1.      จะป้อน Code  เก็บที่ไหน ชื่ออะไร ชนิดไหน ?  …  Source File,Library, Member, Type
2.      เรียกใช้
2.1 ผ่านตัวจัดการ  
strPDM >  3  ป้อน  Source File,Library, Member, Type
2.2 โดยตรง   
STRSEU + [F4] > ป้อน Source File,Library, Member, Type
3.      เริ่มเขียน Code  (ใช้คำสั่งแต่ละพื้นที่)
4.      สิ้นสุดการ เขียน code  โดย กด F3=Exit

ขั้นตอนการเขียน Code (แบบเต็ม)

1.      จะป้อน Code  เก็บที่ไหน ชื่ออะไร ชนิดไหน ?  …  Source File,Library, Member, Type
Source Code 
 เรียก “Member”    Member ต้องจัดเก็บอยู่ใน  Library / Source File



*1   RPG/400  (F-spec) ช่อง File Name   ป้อนได้ไม่เกิน 8 อักษร*2   RPG/400  (C-spec) ช่อง Factor 2    จะ call PGM ที่มีชื่อไม่เกิน 8 อักษรเท่านั้น
2.      เรียกใช้  ผ่าน ตัวจัดการ (คล้าย  เลือกชื่อผ่าน Explorer)   …  (แนะนำให้ใช้ตัวนี้)strPDM >  3  ป้อน  Source File,Library, Member, Type
ป้อนคำสั่ง strpdm + Enter
เลือก 3 + Enter
ป้อน File , Lib  (ตามต้องการ)  Member ใส่ *ALL

ต้องการให้ Member เริ่มแสดงด้วย  Member = SQL  ให้ป้อน Position to (มุมขวาบน)  = SQL 
Tip
          สามารถป้อนโดยใช้คำสั่งเดียวดังนี้   wrkmbrpdm qgpl/qddssrc  sql*
3.      เริ่มเขียน Code
3.1  กำหนดค่าเริ่มต้น  ก่อน  กด F13  =Change Session Defaultแนะนำเมื่อกด เลื่อนหน้า ให้เลื่อนทั้งแผ่น (F=Full)

พื้นที่ใช้งาน
(1) คำสั่งจัดการ ภาพรวม
(2) คำสั่งจัดการ กับ “บรรทัด”
(3) ส่วนของ Code
                (1) คำสั่งจัดการ ภาพรวม   … ใช้บ่อย
(9),(10)

                (2) คำสั่งจัดการ กับ “บรรทัด”  … ใช้บ่อย



(3) ส่วนของ Code

4.      สิ้นสุดการ เขียน code  โดย กด F3=Exitจะเห็น  หน้าจอยืนยัน

 4. สิ้นสุดการเขียน Code โดยการกด F3=Exit จะเห็นหน้าจอ ยืนยัน


 


วันอาทิตย์ที่ 25 พฤศจิกายน พ.ศ. 2555

อ่านโปรแกรม RPG (ปัจจุบัน) ให้เป็น

อ่านโปรแกรม RPG (ปัจจุบัน) ให้เป็น

สำหรับ  โปแกรมเมอร์ใหม่ (0-3 ปี)
หนึ่งในหน้าที่ คือ เรียนรู้ระบบฯงานที่มีอยู่

ผมเชื่อว่า คนสอน >50% จะบอกว่า  ให้เรียนรู้ (อ่านเอาเอง) จากโปรแกรม
บางที่จะมี  เอกสารประกอบให้ดู (ส่วนใหญ่ก็จะเก่ามาก)


ผมก็สอนงาน แบบนี้เหมือนกันครับ  : P
ก็ภาษามันมีโครงสร้าง ไม่ซับซ้อน  ดังนั้นการอ่าน/ทำความเข้าใจ  ก็ยาก
แต่ใช้ไม่ได้กับทุกกรณีครับ  บางเรื่องก็ยากเกินไปน๊ะครับ

ถ้าเป้าหมายของการสอนงาน คือ "เรียนรู้ให้เร็ว" และพร้อมทำงาน
วิธีข้างต้น  ก็ไม่ตอบโจทย์น๊ะครับ

ตย.  นาย ก ต้องมาค้นหาวิธีเขียนโปรแกรมเทคนิค abc   ใช้เวลา 1 เดือน
ด้านดี  (คิดบวกมากๆ) นาย ก เรียนรู้ด้วย "ตนเอง" ได้ (นักเรียนในฝัน)
ด้านเสีย  ใช้เวลานานไป  และไม่การันตีว่า  รู้ตรงกับที่อยากให้เรียนรู้

ความเห็นของผม : แค่ "ลงมือทำ" ในสิ่งที่คิด
(ทุกคนคิดได้  แต่ไม่ได้ลงมือทำ  อ้างว่าไม่ว่างซะงั้น)

กลับมาที่หัวข้อ  วิธีอ่านโปรแกรม (ปัจจุบัน)​ ให้เป็น

Q: เราอ่านโปรแกรม เพื่ออะไร ?

หากฏ หรือ ข้อกำหนด (condition), หาความสัมพันธ์, เรียนรู้เทคนิค
โดยปรกติ จะเริ่มดูโปรแกรม กลุ่ม Entry, Maintenance และหน้าจอผลลัพธ์ที่ใช้บ่อย


หากฏ หรือ ข้อกำหนด (condition)
- ถ้า  สินค้า  เป็นชนิดนี้   ค่า commission ให้ลดลง 5% ... ไม่พบในเอกสารพื้นฐาน

หาความสัมพันธ์
- File A01,A02 ต้องใช้คู่กับ File C05 ... ไม่มีในเอกสาร



เทคนิคต่างๆ
- แสดงผลแบบ xxx (ต้องใช้ 3 เทคนิค) ... ไม่มีในหนังสือ
- Array, SubFile ... หนังสือทั่วไปไม่พูดถึง

มา Share ประสพการณ์จากผมทีละเรื่องกันครับ

หากฏ หรือ ข้อกำหนด (condition)


ปรกติ  ผมจะเริ่มจาก สรุปหลักการขึ้น  "แบบย่อ" ขึ้นมาก่อนครับ (ประมาณครึ่งหน้า A4)
เช่น งานบัญชี Fix Asset,  งานผลิต   มี keyword อะไรบ้าง

ตย.1
       สินทรัพย์ ทุกรายการต้องมี  เลขที่ประจำตัว  บอกวันเริ่มต้น, ระยะเวลาหมดค่า, สูตรการคำนวน

เมื่ออ่านโปรแกรม อาจจะพบว่า  มีกลุ่มสินทรัพย์  บางกลุ่มต้องใช้สูตรที่แตกต่างไป (เช่น ต้องลดค่าลงแบบเรขาคณิต)  ให้บันทึกเป็น "ข้อกำหนดพิเศษ"

       สินทรัพย์ ทุกรายการต้องมี  เลขที่ประจำตัว  บอกวันเริ่มต้น, ระยะเวลาหมดค่า, สูตรการคำนวน
แบบเชิงเส้น
       ยกเว้น กลุ่มอิเลคทรอนิคส์  (รหัส E) ใช้สูตรลดค่าแบบเรขาคณิต

ตย.2
       งานผลิต  ชิ้นงาน (Item No)  ผลิตที่  (Process, machine) ณ. เวลา (วันเดือนปี,เวลา)  ได้จำนวนดี
,เสียหาย เท่าไหร่  

เมื่ออ่านโปรแกรม อาจจะพบว่า  Process A321  จะบันทึกจำนวนในรูปแบบกิโล  แทนการนับเป็นชิ้น  ให้บันทึกเป็น "ข้อกำหนดพิเศษ" 

       งานผลิต  ชิ้นงาน (Item No)  ผลิตที่  (Process, machine) ณ. เวลา (วันเดือนปี,เวลา)  ได้จำนวนดี,เสียหาย เท่าไหร่ (นับเป็นชิ้น)
       ยกเว้น  Process A321 ที่นับเป็น กิโล (ต้องเปลี่ยน กิโล เป็น ชิ้น)

หาความสัมพันธ์ (ER)


ในมหาวิทยาลัยเราเรียนการสร้าง  Table ใหม่  แต่ในงานจริง บริษัทฯ เปิดทำการมามากกว่า 20 ปี
Table ต่างๆ ถูกสร้างขึ้นมามากมายแล้ว   ดังนั้นการอ่านโปรแกรมก็คือ การหาความสัมพันธ์  ที่
อาจจะไม่เคยเห็นเอกสาร   พูดภาษาชาวบ้านคือ "เดาใจ"  ผู้สร้าง Table ในยุคนั้นออกแบบอย่างไร
โชคไม่ดี  ถ้า Developer ที่จบมาส่วนใหญ่  ออกแบบไม่เก่ง  ในยุคสมัยนั้น คงไม่ต่างกันเท่าไหร่ ...
การเดาใจจึง "ซับซ้อน"  กว่าปรกติ

อันดับแรก ต้องเชื่อว่าโปรแกรมที่กำลังอ่าน   สร้างถูกต้องตามหลักการก่อน
(ไม่จำเป็นต้องถูกต้อง 100% ก็ได้)
ตัวเอกสาร Fix Asset, งานผลิต  ถ้าจะสร้าง Table ใหม่  น่าจะต้องใช้กี่ Table
(เริ่มจาก 1 Table  -> หลาย  Table ที่สัมพันธ์กัน)  ไม่เรียกเดาใจจะเรียกอะไรครับ

เรื่องถัดมา  เข้าใจวิธี RPG เรียกใช้ DB2/400 

DB2/400 เป็น Database ที่ดีอีกตัว  ง่ายเมื่อทำงานกับ 1 Table เริ่มซับซ้อนเมื่อทำงานมากกว่า 1 Table  และ เริ่มเข้าใจยากเมื่อ บันทึก/แก้ไข ข้อมูลหลาย Table  (ยุคนี้ทำได้ง่ายขึ้น แต่ก็ยังไม่ง่ายจริงๆ)  
ดังนั้นไม่ใช่เรื่องแปลกที่ Developer ในยุคนั้นจะเลือกวิธีที่ง่าย  มากกว่าวิธีที่ถูกต้องจริงๆ (ซึ่งทำได้ยุ่งยาก)  เช่น ยุคนี้  SQL  3 Table ในครั้งเดียว  RPG ยุคใหม่จะมองผลลัพธ์เป็น 1 Table แต่ RPG ในยุคดังกล่าวจะทำทีละ Step (อ่าน Table แรก  แล้วนำแต่ละ Row ไปอ่าน Table 2 หรือ 3) 

ลองดู ตย. การเขียนในสไตล์ใหม่
SQL : -> ได้ 1 Table-Result   
     Select  *   from    Table-1 as T1   
     Left Join   Table-2  as T2 On  T1.X=T2.Y 
     Left Join   Table-3  as T3 On  T1.Y=T3.Z

RPG : (ที่ใช้คู่กับ SQL)  ทำให้ดูง่าย
     Read  Table-Result
     Do while    not EOF
           Print
           Read  Table-Result
     End Do

ตย.นี้  เรารู้แค่ Table ทั้ง 3 มี Relation  แต่ไม่รู้ว่าความสัมพันธ์เป็นแบบไหน
1:1 หรือ 1:m    ต้องทดสอบโดยต้องใช้ SQL หา Relation ทีละคู่
  Table-1  ได้ผลลัพธ์  115 rows 
เพิ่ม Join กับ Table-2  ถ้านับผลลัพธ์ได้ 115 rows เท่าเดิม -> จะได้ relation  1:1
เพิ่ม Join กับ Table-2  ถ้านับผลลัพธ์ได้มากกว่า  115 rows  -> จะได้ relation  1:m
-----------------------------------------------------------
ลองดู ตย. การเขียนที่สืบทอดกันมา
RPG :
     Pointer to  Table-1
     Read  Table-1  (one row)
     Do while    not EOF
           T1.x  Pointer to  Table-2  

           T1.y  Pointer to  Table-3
           Read  Table-3  (one row)
           Do while    not EOF
               Print
               Read  Table-3  (next row)
           End Do

           Read  Table-1  (next row)
     End Do


ตย.ข้างต้น เราจะอ่าน Relation ได้ว่า 
     Table-1 : Table-2 จะมีความสัมพันธ์ แบบ 1:1
     Table-1 : Table-3 จะมีความสัมพันธ์ แบบ 1:m

หัวข้อถัดไปยกยอดไปคราวหน้าแล้วกันครับ (เนื้อหาเริ่มยาวเกินไปแล้ว)