วันพฤหัสบดีที่ 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 
  • ปัญหาเรื่องความเคยชิน  ของโปรแกรมเมอร์ ที่ทำงานมานาน (ช้า เมื่อย้ายไปเขียน  วิธีใหม่)

ไม่มีความคิดเห็น:

แสดงความคิดเห็น