04_GENERO 2.5语法基础

Genero FGL 学习

Genero FGL 简介

Genero FGL 语言,为 Four J’s(http://www.4js.com)于 2004 年所发表。整体结构为承袭 INFORMIX 数据库的 4GL 管理语言而来,即 INFORMIX-4GL 。

INFORMIX-4GL 属于第四代架构的语言,其优点在于构成程序的语法和英文近似,可以大幅减少学习的时间,但仅能使用于 INFORMIX 数据库的控制上。

Four J’s 取其优点,为了能够应用于更多后端数据库,开发出 FGL 语言,并因应图型化,改版为Genero FGL,有以下特点:

  • 切分为 Client、Server 架构(GDC 与 fgl),增进执行效率
  • 以 XML Bsae 做为 Client 及 Server 端数据传递的架构
  • 支援更多不同平台(OS)及数据库系统
  • 可在运行时间动态调整画面输出的格式(Layout Styles)
  • 引入基本的 对象(Object) 概念

在这里插入图片描述
在 Genero FGL 语言架构中,将 程序逻辑、画面 视为不同的项目,分别撰写。
Program(程序)= MODULE(逻辑代码) + FORM(画面代码)
在这里插入图片描述
由上图可知:

  1. Program 可由许多的 Module 与 Form 构成。
  2. 单一的 4GL 由一个或一个以上的 Function、Report 组成。
  3. 一个完整的 Program 中,必需指定一个『Main』作为程序执行入口。

Genero FGL 开发(编译、连接、执行)

当程序及画面编写完成后,还需经过编绎(Compile)连结(Link),才能够被执行(Execute)
编译需要 Genero Development License,连结及执行需要 Genero Runtime License。

编译流程
在这里插入图片描述
程序的原始文件后缀名为 .4gl,经过编译后会产生后缀为 .42m 的文件。
程序(Program)的编译fglcomp [编译参数] 待编译文件名[.4gl]

编译前处理(preprcessor) ????????

GeneroFGL 可直接以 文字编辑程序如Vim 进行 文字格式(per 档)的开发;
也可使用 Genero Studio 内的『Form Design』功能开发 XML格式(4fd 档)的画面;
编译后都会产生后缀名为 .42f 的文件。

文字格式(per文件)编译fglform [编译参数] 待编译文件名[.per]
XML 格式(4fd文件)编译gsform [编译参数] 待编译文件名[.4fd]


连结流程

若该程序不需要使用其他 4gl 提供的功能,可以略过连接的程序,直接执行作业。
若该作业被切分成许多子程序,则可在执行连接前,先将子程序打包成一组动态链接函式库(Dynamic Link Library),后缀名为 .42x,打包完成后再与原始作业进行连接。
在这里插入图片描述
连接语法:fgllink -o 连结后的完整文件名 待连结文件1 [待连结文件2] ….

示例:

  1. fgllink -o test.42x test1.42m test2.42m test3.42m(生成42x文件)
  2. fgllink -o program.42r test.42x main.42m others.42m(生成42r文件)

注:画面文件不需要执行链接


执行程序

执行程序前,需先开启使用者端的 Genero 桌面客户端软件(GDC:Genero Desktop
Client),以令主机端的 fglrun 可以与客户端的『GDC』进行沟通。
在这里插入图片描述
执行指令:fglrun [执行参数] 执行文件名[.42r]

当程序未使用到其他外部资源时,也可以直接执行含 MAIN 函数的 .42m 文件。

程序执行的过程中,所有逻辑运算均于主机端执行,只有画面异动数据会以连续的 XML 封包传递到客户端,经过 GDC 解译重组后,与使用者进行互动。

fglrun -V 可以查看 fgl 的版本号。
在这里插入图片描述

第一个程序 Hello World

MAIN
	DISPLAY "hello world!!"
END MAIN

在这里插入图片描述

4GL中的 注释

  • { } 可以将某个范围做备注
  • #-- 将某行做备注

变量与运算符

变量类型
在这里插入图片描述

变量定义(DEFINE)

直接定义变量DEFINE 变量名 变量类型
定义变量对应数据库字段DEFINE 变量名 LIKE 数据表.数据字段

-- 直接定义 employee_no 变量, 类型是 CHAR(10)
DEFINE employee_no CHAR(10)

-- 一次性定义多个变量并用","号隔开
DEFINE p_join_date,p_birthday DATE

-- 定义 p_employee_no 和数据库对应
DEFINE p_employee_no LIKE employee_file.employee_no,
	   p_team_no SMALLINT,
	   p_join_date, p_birthday DATE

预定义变量

Genero 的预定义变量及用途,可以直接使用。
在这里插入图片描述

STRING字符主法

变量集合(RECORD )

直接定义变量集合(Records)
例:直接定义 rec 这个 Records 中的各个变量类型。

DEFINE rec RECORD --定义rec这个变量,它是个RECORD,是个变量集合
	id INTEGER,
	name VARCHAR(100),
	birth DATE
END RECORD

定义变量集合对应数据库字段
例:定义 cust01 这个 Record 的变量与数据库中的 customer 这个 table 的字段有相同的名称及数据类型,有以下两种方法。

DATABASE example_database

MAIN
	DEFINE cust01 RECORD LIKE customer.*
END MAIN
# 此范例中,定义cust02这个Record的变量与数据库中的customer这个table的字段有相同的名称及数据型态
DATABASE example_database

MAIN
	DEFINE cust02 RECORD
		id LIKE customer.id,
		name LIKE customer.name,
		birth LIKE customer.birth,
		sales LIKE salesman.name
	END RECORD
END MAIN

数据结构(TYPE)

使用数据结构TYPE[PRIVATE|PUBLIC] TYPE 变量名 变量类型

# 程序开发时可以定义数据结构,以便在后续的函式内快速定义相同结构的变量或变量组
# 可定义成public(预设)/private

PUBLIC TYPE customer RECORD
	cust_num INTEGER,
 	cust_name VARCHAR(50),
 	cust_addr VARCHAR(200)
END RECORD
DEFINE c customer # 使用数据结构customer 并取名为 c
PUBLIC TYPE rpt_order RECORD
	order_num INTEGER,
	store_num INTEGER,
	order_date DATE,
	fac_code CHAR(3)
END RECORD

MAIN
	DEFINE o rpt_order #使用数据结构 rpt_order 并取名为 o
	DECLARE order_c CURSOR FOR
	SELECT order_num, store_num, order_date, fac_code FROM orders
	START REPORT order_list
	FOREACH order_c INTO o.*
	OUTPUT TO REPORT order_list(o.*)
	END FOREACH
	FINISH REPORT order_list
END MAIN

变量赋值(LET)、初始化(INITIALIZE)

变量赋值LET varibale = expression

MAIN
	DEFINE c1, c2 CHAR(10)
	LET c1 = "Genero"
	LET c2 = c1
END MAIN

注:若变量形态为 CHAR 和 VARCHAR 时,指定给予的值有差异。
在这里插入图片描述


初始化变量集合(INITIALIZE)
用于初始化一组 RECORD 变量为 NULL,或者是初始化为数据库 Table 的默认值:

INITIALIZE 变量串行 { LIKE 字段串行 | TO NULL }

# 示例
MAIN
	DEFINE cr RECORD LIKE customer.*
	INITIALIZE cr.cust_name TO NULL --初始化cr的cust_name字段为NULL
	INITIALIZE cr.* LIKE customer.* --初始化cr的变量为customer表的值
END MAIN

常数(CONSTANT)

一些系统预定义常数
在这里插入图片描述
定义常数CONSTANT constant_id [data_type] = value
注:定义常数可以不指定类型;系统会自动判断类型,若指定错误则会纠正。

CONSTANT c1 = "Drink" -- 自行定义为 STRING
CONSTANT c2 = 4711 -- 自行定义为 INTEGER
CONSTANT c3 SMALLINT = 12000 -- 自行纠正为 INTEGER
CONSTANT c4 CHAR(10) = "abc" --  按照定义为 CHAR(10)

运算符(operators)

比较运算符
`在这里插入图片描述

MAIN
    DEFINE a,b INTEGER
    LET a = b := 10
    DISPLAY a, b -- 10  10
END MAIN

--String字符串公共函数:cl_str
IF cl_null(l_a) THEN

END IF

IF NOT cl_null(l_a) THEN

END IF
MAIN
    DEFINE a,b INTEGER
    LET a = b = 10
    DISPLAY a,b -- 0   0
END MAIN

逻辑运算符
在这里插入图片描述
数值运算符
在这里插入图片描述

MAIN
	DEFINE i,j SMALLINT
	LET i = 9
	LET j = 2
	DISPLAY i + j	--DISPLAY 11
	DISPLAY i - j	--DISPLAY 7
	DISPLAY i * j	--DISPLAY 18
	DISPLAY i / j	--DISPLAY 4.5
	DISPLAY j ** i	--DISPLAY 512
	DISPLAY i mod j	--DISPLAY 1
END MAIN

字符串运算符
在这里插入图片描述
在这里插入图片描述
说明:表达式[start,end]表示从字符串中取出子字符串,此表示方式仅能用在 CHAR 或
VARCHAR 上
,若变量型态为 STRING,则参照如下范例:

MAIN
	DEFINE i,j STRING
	LET i = "T100"
	LET j = i.subString(1, 4)
	DISPLAY j	--T100
	DISPLAY i.subString(1, 4) --T100
END MAIN

在这里插入图片描述
关联语法(Associative syntax) 运算符
在这里插入图片描述

日期运算符
在这里插入图片描述
对话框处理 (Dialog handling)运算符
在这里插入图片描述

全局变量(GLOBALS)

语法一:直接写定 GLOBALS 区块

GLOBALS
	declaration-statement
	[,...]
END GLOBALS

语法二:读入已写好的共同配置文件(外部档案)

GLOBALS "filename"

变数的生命周期(LOCALE、MODULE、GLOBAL)

LOCAL变量(Local Variables)

  • 定义位置:定义在 Module 中的函式里 (MAIN、FUNCTION 等)
  • 生命周期:只属于该定义的函式使用,离开此函式即不能再使用。

MODULE变量(Module Variables)

  • 定义位置:Module 中,但不被任何的函式包围。
  • 生命周期:为该 Module 中的共享变数。

GLOBAL变量(Global Variables)

  • 定义位置:由 GLOBALS 及 END GLOBALS 所包围的变数。
  • 生命周期:使用的所有 MODULE 的共享变量。
SCHEMA ds

GLOBALS
	DEFINE g_employee CHAR(10) --GLOBAL
END GLOBALS

DEFINE g_tty CHAR(32) --MODULE

MAIN
	DEFINE answer CHAR(1) --LOCAL
END MAIN

FUNCTION ins_employee()
	DEFINE flag CHAR(1), --LOCAL
	change SMALLINT	--LOCAL
END FUNCTION

控制输出格式(USING)

针对 数值或日期 设定其 显示格式,设定时注意 溢出(overflow) 的问题。

数值格式标志
在这里插入图片描述
日期格式标志
在这里插入图片描述

LET salary =1000
DISPLAY salary  USING " $##,### "  --Show:  $ 1,000
DISPLAY "yyyy-mm-dd :",TODAY USING "yyyy-mm-dd" --Show:  yyyy-mm-dd :2004-10-11
MAIN
	DEFINE i,j SMALLINT
	LET i = 12345
	LET j = -12345
	DISPLAY i
	DISPLAY j
	DISPLAY i USING"*******"
	DISPLAY j USING"*******"
	DISPLAY i USING"&&&&&&&"
	DISPLAY j USING"&&&&&&&"
	DISPLAY i USING"#######"
	DISPLAY j USING"#######"
	DISPLAY i USING"<<<<<<<"
	DISPLAY j USING"<<<<<<<"
	DISPLAY i USING"-------"
	DISPLAY j USING"-------"
	DISPLAY i USING"+++++++"
	DISPLAY j USING"+++++++"
	DISPLAY i USING"$$$$$$$"
	DISPLAY j USING"$$$$$$$"
	DISPLAY i USING"(######)"
	DISPLAY j USING"(######)"
	DISPLAY i USING"###,###.&&"
	DISPLAY j USING"###,###.&&"
END MAIN

在这里插入图片描述

MAIN
	DISPLAY TODAY
	DISPLAY TODAY USING "yyyy-mm-dd"
	DISPLAY TODAY USING "yy-mm-dd"
	DISPLAY TODAY USING "yy-mmm-ddd"
END MAIN

在这里插入图片描述

CLIPPED:删除字符串右侧空白

LET i = "TIPTOP GP                 "
LET j = "Genero FGL"
DISPLAY "i&j=", i CLIPPED, j --Show: i&j=TIPTOP GPGenero FGL

SPACES:输出空白字符串

DEFINE i,j CHAR(10)
LET i = "TIPTOP GP"
LET j = "Genero FGL"
DISPLAY “i&j=", i ,8 SPACES, j CLIPPED 
--Show: i&j= TIPTOP GP         Genero FGL

函数、流程控制

IMPORT 引入链接库:汇入套件或其他程式库 ( C / Java / FGL )

语法:

需要汇入4GL函式库时 IMPORT  FGL  42m_modulde

需要汇入JAVA函式库时 IMPORT  JAVA  class_name

內建套件﹝built-in package,使用前不需要IMPORT﹞: base (讀寫檔案、字串處理、訊息使用與處理) ui (視窗畫面、對話框、拖拉及下拉式選單元件) om (XML處理)

延伸套件﹝extension package,使用前需要IMPORT﹞: os (檔案及目錄處理) util (數學及JSON文件處理) com (WEB及通訊處理) security (安全演算法處理)

IMPORT util
MAIN
    DEFINE cust_rec RECORD
               cust_num INTEGER,
               cust_name VARCHAR(30),
               orderids DYNAMIC ARRAY OF INTEGER
           END RECORD
    DEFINE js STRING
    CALL util.JSON.parse( js, cust_rec )   #從JS內把資料取入陣列
    LET js = util.JSON.stringify( cust_rec )  #從cust_rec 把資料寫入JS
END MAIN

SCHEMA / DATABASE 声明数据库

使用 SCHEMA 声明数据库时,只会在编译过程中使用到声明的功能,执行程序时并不会与数据库进行实质联机。
使用 DATABASE 声明数据库时,编译时功能相同,但执行时即会链接数据库。

在这里插入图片描述

SCHEMA ds #声明数据库ds

MAIN
	DEFINE lc_zz01 LIKE zz_file.zz01
	SELECT zz01 INTO lc_zz01 FROM zz_file WHERE zz01='tiptop'
END MAIN


注:使用 SCHEMA 指令,因此并未于实际登入数据库,执行时回报如下:

Program stopped at 'test_schema.4gl', line number 5.
SQL statement error number -1803 (-1). Connection does not exist.

GLOBAL:程式所使用的外部变量 

MAIN 函数与设定区块

MAIN 函数是程序执行的入口,一个完整可执行的程序只能有一个 MAIN 函数。

最简单的完整作业:

MAIN
	DISPLAY "hello world!"
END MAIN

MAIN 函数可简单写成如上,也可增加一些 设定区块

DEFER 设定 可定义程序是否要拦截『当使用者按下中断(interrupt)或离开(quit)键』时所送出的系统讯号:DEFER {INTERRUPT | QUIT}

OPTIONS 设定 可变更系统默认的选项
在这里插入图片描述
Exceptions 设定 定义当遇到 SQL 错误时,系统要采什么操作。
语法格式:WHENEVER [ANY] ERROR { CONTINUE | STOP | CALL function | GOTO label }

复杂的 MAIN 函数示例:

MAIN
	OPTIONS #改变一些系统默认值
		INPUT NO WRAP, #输入的方式:不打转
		# FORM LINE FIRST + 2, #画面开始的位置
		# MESSAGE LINE LAST, #讯息显示的位置
		# PROMPT LINE LAST, #提示讯息的位置
		FIELD ORDER FORM #整个画面会依照 p_per 所设定的字段顺序
	DEFER INTERRUPT
	
	WHENEVER ERROR STOP #当发生 SQL Error 时即停止程序
	
	DISPLAY “Change Exception!”
	
	WHENEVER ERROR CALL chk_err #此处的 CALL 是没有括号的
	
END MAIN

FUNCTION chk_err( )
	DISPLAY “Error Happened!”
END FUNCTION

 

FUNCTION 函数

语法:[PUBLIC | PRIVATE ] FUNCTION function_name( [arg [ , … ] ] )

当设定为 PRIVATE 时,只限本 4gl 文件内使用;连结时在 42x 或 42r 内无法看到该函数。

MAIN
	CALL say_hello_public()
	CALL say_hello_private()
END MAIN

FUNCTION say_hello_public()
	DISPLAY "Hello, world!"
END FUNCTION

PRIVATE FUNCTION say_hello_private()
	DISPLAY "Hello, Private!"
END FUNCTION

CALL 执行指定的函数,若有回传值,以 RETURNING 接回。
语法格式:CALL function ( [ parameter [,...] ] ) [ RETURNING variable [,...] ]

RETURN 返回函数所需的变量值,并停止此函式的执行。


回传单一值

MAIN
	DEFINE var1 CHAR(10)
	DEFINE var2 CHAR(2)
	LET var1 = foo()
	DISPLAY "var1 = " || var1 -- var1 = Hello
	CALL foo() RETURNING var2
	DISPLAY "var2 = " || var2 -- var2 = He
	DISPLAY "foo() = " foo() -- foo() = Hello
END MAIN

FUNCTION foo()
	RETURN "Hello"
END FUNCTION

回传单一值(布尔):

MAIN
	IF foo() THEN
		DISPLAY "Choice is OK!”
	END IF
END MAIN

FUNCTION foo()
	RETURN TRUE
END FUNCTION

回传多个值: 可用 RETURNING 接收

MAIN
	DEFINE var1 CHAR(15)
	DEFINE var2 CHAR(15)
	CALL foo() RETURNING var1, var2
	DISPLAY var1, var2
END MAIN

FUNCTION foo()
	DEFINE r1 CHAR(15)
	DEFINE r2 CHAR(15)
	LET r1 = "return value 1"
	LET r2 = "return value 2"
	RETURN r1, r2
END FUNCTION

文件之间互相调用:test1.4gl 调用 test2.4gl 中的函数,并且用命令行执行 test3.4gl

test1.4gl

MAIN
	DISPLAY "MAIN FUNCTION"
	CALL a1()
	CALL a2()
	RUN "fglrun test3" # 利用RUN执行命令行
END MAIN

FUNCTION a1()
	DISPLAY "SUB FUNCTION a1()"
END FUNCTION

以上程序段会呼叫另外两个 Function,并且利用 RUN 指令执行一道 unix 指令

test2.4gl

FUNCTION a2()
	# a2 function
	DISPLAY "SUB FUNCTION a2()"
END FUNCTION

test3.4gl

MAIN
	DISPLAY "This is test3.4gl"
END MAIN

报表结构 REPORT 函数(跳过)

这是一种专门用来设定报表打印格式的函数,后续章节有详细的介绍。

注释(批注)

{  }:可以将某个范围做备注; # :将某行做备注;建议使用此符号 -- 将某行做备注

IF

语法:MATCHES 支持正则表达式

IF condition THEN
	statement
	[...]
ELSE
	statement
	[...]
END IF
MAIN
	DEFINE name CHAR(20)
	LET name = "John Smith"
	IF name MATCHES "John*" THEN
		DISPLAY "The first name is too common to be displayed."
		IF name MATCHES "*Smith" THEN
			DISPLAY "Even the last name is too common to be displayed."
		END IF
	ELSE
		DISPLAY "The name is " , name , "."
	END IF
END MAIN

CASE

语法1格式:适用于判断式比较简单,如只需判断1个数字

CASE expression-1
	WHEN expression-2
		{ statement | EXIT CASE }
		[...]
	OTHERWISE
		{ statement | EXIT CASE }
		[...]
END CASE
MAIN
	DEFINE v CHAR(10)
	LET v = "C1"
	CASE v
		WHEN "C1"
			DISPLAY "Value is C1"
		WHEN "C2"
			DISPLAY "Value is C2"
		WHEN "C3"
			DISPLAY "Value is C3"
		OTHERWISE
			DISPLAY "Unexpected value"
	END CASE
END MAIN

语法2格式:适用于判断式较复杂

CASE
	WHEN boolean-expression
		{ statement | EXIT CASE }
		[...]
	OTHERWISE
		{ statement | EXIT CASE }
		[...]
END CASE
MAIN
	DEFINE v CHAR(10)
	LET v = "C1"
	CASE
		WHEN ( v="C1" OR v="C2" )
			DISPLAY "Value is either C1 or C2"
		WHEN ( v="C3" OR v="C4" )
			DISPLAY "Value is either C3 or C4"
		OTHERWISE
			DISPLAY "Unexpected value"
	END CASE
END MAIN

语法2注意:若有两种情况成立,系统仅会走第一条符合条件的路径。

下列代码只会显示:a is ok

MAIN
	DEFINE a,b INTEGER
	LET a = b := 10
	CASE
		WHEN a = 10 DISPLAY " a is ok "
		WHEN b = 20 DISPLAY " b is ok "
		OTHERWISE DISPLAY " nothing is ok "
	END CASE
END MAIN

FOR

语法:默认情况 STEP = 1

FOR counter = a TO b [ STEP value ]
	statement
	[...]
END FOR
MAIN
	DEFINE i, i_min, i_max INTEGER
	LET i_min = 1
	LET i_max = 10
	
	DISPLAY "Look how well I can count from " , i_min , " to " , i_max
	DISPLAY "I can count forwards..."
	
		FOR i = i_min TO i_max # 正序
			DISPLAY i
		END FOR
		
		DISPLAY "... and backwards!"
		
		FOR i = i_max TO i_min STEP -1 #反序
			DISPLAY i
		END FOR
END MAIN

WHILE

语法:执行程序直到条件不成立为止。

WHILE b-expression
	 statement
	 [...]
END WHILE
MAIN
	DEFINE a,b INTEGER
	LET a = 20
	LET b = 1
	
	WHILE a > b
		DISPLAY a , " > " , b
		LET b = b + 1
	END WHILE
	
END MAIN

CONTINUE

语法:跳出本次循环。

CONTINUE { FOR | FOREACH | MENU | CONSTRUCT | INPUT | WHILE }
MAIN
	DEFINE i INTEGER
	LET i = 0
	WHILE i < 5
		LET i = i + 1
		DISPLAY "i = " || i
		CONTINUE WHILE
		DISPLAY "This will never be displayed !"
	END WHILE
END MAIN

EXIT

语法:离开控制段。

EXIT { CASE | FOR | MENU | CONSTRUCT | FOREACH | REPORT | DISPLAY | INPUT | WHILE | PROGRAM }
MAIN
	DEFINE i INTEGER
	LET i = 0
	
	WHILE TRUE
		DISPLAY "This is an infinite loop. How would you get out of here ?"
		LET i = i + 1
		IF i = 100 THEN
			EXIT WHILE
		END IF
	END WHILE
	
	DISPLAY "Well done."
END MAIN

TRY…CATCH

针对重要 SQL 指令可以预先设置专用的错误处理指令。

语法:

TRY
	[待侦测是否会发生问题的程序段落]
CATCH
	[处理问题或回报讯息的程序段落]
END TRY

示例:

MAIN
	LET lc_a = ARG_VAL(1)
	TRY
		DATABASE lc_a
	CATCH
		DISPLAY lc_a,' not in fglprofile, number:', SQLCA.SQLCODE
	END TRY
END MAIN

SLEEP

语法:程序依指定秒数暂停。

SLEEP seconds

注意:如果 seconds < 0 或 seconds IS NULL,则程序不会停止。

MAIN
	DISPLAY "Please wait 5 seconds..."
	SLEEP 5 #暂停5秒
	DISPLAY "Thank you."
END MAIN

LABEL 和 GOTO(❌)

为了程序的易读性和结构性,请不要使用该指令。

MAIN
	DISPLAY "Before GOTO"
	GOTO: label_id1
	DISPLAY "Never Been Displayed"
	LABEL label_id1:
	DISPLAY "After GOTO"
END MAIN

常用的内置函数(built-in functions)

在这里插入图片描述

开启画面OPEN WINDOW

语法 : OPEN WINDOW window_name [AT line,column ] WITH [FORM form-file | height ROWS,width COLUMNS ] [ ATTRIBUTES ( window-attributes ) ] 

说明:  window-id:定义这个window name名称。  AT line, column:表示让画面开启的起始坐标,仅限控制台﹝Console﹞生效。  form-file:经过编译后的画面档文件名(不含附檔名),之前可以指定放置路径。  height ROWS, width COLUMNS:指定画面占用行数及栏数。  ATTRIBUTES ( window-attributes ):可以加上属性设定

清除指定资料栏位CLEAR

# 语法 :
CLEAR  field-list
Ex:
FOR i=1 TO 10
               CLEAR g_employee[i].*
END FOR

清除画面CLEAR FORM

# 语法 :
CLEAR FORM
Ex:
OPEN WINDOW w1 AT 1,1 WITH FORM "employee"
     CLEAR FORM
                 :
     CLOSE WINDOW w1

切换视窗CURRENT WINDOW 

# 语法 :
CURRENT WINDOW IS window-name
Ex:
  OPEN WINDOW w1 WITH FORM "employee“
  OPEN WINDOW w2 WITH FORM “boss“

   MENU
           ON ACTION to_emp
                   CURRENT WINDOW IS w1
           ON ACTION to_boss
                   CURRENT WINDOW IS w2
   END MENU

  CLOSE WINDOW w1
  CLOSE WINDOW w2

关闭窗口CLOSE WINDOW  

# 语法 :
CLOSE WINDOW window-name 
Ex:
  OPEN WINDOW w1 AT 1,1 WITH FORM "employee"
   CLEAR FORM
                 :
   CLOSE WINDOW w1

选单MENU

 

 

 

 

 

 

 

CURSOR

在 Genero FGL 语言中,当下达 DATABASE dstabase_id 指令后,即与数据库执行链接,也就是可以开始进行数据的 查询(SELECT)、更改(UPDATE)、新增(INSERT)等 动作。

在处理数据时若只有 单笔数据 的选取,则可用 单纯的 SQL 指令即可

若是要抓取 多笔数据 处理时,就会因抓取的特性不同(例如:一次只抓一笔数据处理,完成后再抓次笔,如『个人资料表』;或一次全部抓取,一起编辑,如『个人门禁进出记录表』等),而需使用不同的 指标(CURSOR)

CONSTRUCT:获取用户输入组成【WHERE条件】

作用:让用户在画面上输入查询条件(通称 Query By Example;QBE),以取得用户的查询范围数据。

用户的查询数据会组成一串 【WHERE 条件】,并置入设定好的变量中。若使用者未输入任何条件,即按下『确定』离开 CONSTRUCT,系统也会自动于此变量中补入 1=1

在这里插入图片描述

  • 域名column_list变量名field_list 相同时,可以采用CONSTRUCT BY NAM
  • char_variable 为接取用户输入数据的 字符串变量(建议以 STRING 格式变量接取)
  • column_list 为对应到表格(TABLE)的域名清单(逗号隔开)
  • field_list 为画面(WINDOW 或是 FORM)上的字段代码清单(逗号隔开)
  • 若有增加 控制区段 (CONTROL BLOCK,如ON ACTION等),则就要加上END CONSTRUCT

语法1示例:

CONSTRUCT BY NAME l_str ON employee, salary
	ON IDLE 10
	EXIT PROGRAM
END CONSTRUCT

在这里插入图片描述
经过上图用户输入的条件,在程序中进行了拼接,形成一个【WHERE 条件】:

l_str = "employee='1000" AND salary>30000"

后续在组 SQL 指令时,即可通过此变量组出符合条件的 SQL 查询指令。

PREPARE:将【SQL字符串】转成【可执行SQL】

若已经得到一个完整的【WHERE 条件】后,接下来即可将此条件,组合成 SQL 字符串,再转换为一个完整且可以抓取符合条件的 SQL 指令。

执行完 CONSTRUCT 后,系统只能得到一个 SQL 字符串,并非为『可执行的指令』,因此必须通过 PREPARE 指令,将此 SQL 字符串转换成『可执行的 SQL 指令』。

PREPARE 会将字符串传入数据库查核语法的正确性,再回传一个 prepared-id 后续调用。
在这里插入图片描述

  • statement-name 是一个 PREPARE 完成后的 可执行SQL(prepared-id)
  • 执行 PREPARE 指令前必须先拼接好 SELECT 语句。
LET l_sql = "SELECT * FROM employee_file",
			"WHERE", l_str        --l_str 是经过 CONSTRUCT 得到的【WHERE条件】
PREPARE emp_pre FROM l_sql

FREE:释放 PREPARE 的记录

FREE 用于 释放 PREPARE 的记录
语法:FREE statement-name

LET l_str = " employee='1000' AND salary>'30000' "
LET l_sql = " SELECT * FROM employee_file WHERE ", l_str
PREPARE emp_pre FROM l_sql
...
FREE emp_pre

数据的查询

Genero FGL 中有两种查询用的指标(CURSOR)可以运用在资料的查询

  1. SCROLLING CURSOR
  2. Non-SCROLLING CURSOR

SCROLLING CURSOR 通常运用在 单文件控制 或 查询类 的程序,如『个人资料表』般的作业,可以随机抓取数据,一次一笔,再处理完后可以选择 往前一比往后一笔往这个查询序列中的任何一笔数据 移动的指针(CURSOR)。

DECLARE cursor_id SCROLL CURSOR [WITH HOLD] FOR sql statement
OPEN cursor_id [USING value]
FETCH [first|last|previous|next| cursor_id INTO variable
CLOSE cursor_id

Non-SCROLLING CURSOR 通常运用在 双文件控制程序 或 报表 程序,如『个人出缺勤统计表』般的作业,抓取数据是 依序 (seguential) 的方式,一次可以将合条件要求的资料一笔接着一笔的抓出,直到资料全数抓完(或被强制终止)为止。

DECLARE cursor_id CURSOR [WITH HOLD] FOR sql statement
FOREACH cursor_id
[USING value]
[INTO variable ]
	...
END FOREACH

SCROLLING CURSOR(DECLARE、OPEN、FETCH、CLOSE)

说明:通常用于 查询程序,可以 随即抓取资料

  1. 通过 DECLARE 定义
  2. 利用 OPEN 开启该 CURSOR
    例:OPEN test01_cursor
  3. 通过 FETCH cursor_name INTO 变量 抓取资料

DECLARE 概述
语法1:使用 已知的SQL语句 进行 CURSOR 声明

DECLARE cursor_name SCROLL CURSOR [WITH HOLD] 
	FOR sql_statement

语法2:使用 已声明的PREPARED ID 来进行 CURSOR 声明

DECLARE cursor_name SCROLL CURSOR [WITH HOLD] 
	FOR prepared_id

语法3:使用 已知的STRING语句 进行 CURSOR 声明

DECLARE cursor_name SCROLL CURSOR [WITH HOLD] 
	FROM string-expression

 

在这里插入图片描述

OPEN 开启游标
语法:

OPEN cursor_name [USING 变量名称]
  • 本指令可用 STATUS 来检查是否执行成功
  • 使用 cursor 之前要先 OPEN CURSOR,开启游标
  • 此语句仅决定符合的数据,并不是真正从数据库中抓取数据

示例:

LET g_sql = "SELECT * FROM cta_file WHERE ROWID = ?" 

DECLARE cta_cl CURSOR FROM g_sql 
			:
OPEN cta_cl USING l_ata01

FETCH 抓取数据
语法:

FETCH cursor_id INTO program_variable

 FETCH 
   [ 
     NEXT | PREVIOUS | FIRST | LAST | 
     RELATIVE expr | ABSOLUTE expr 
    ]
     cursorName [ INTO variableList ] 

当声明为 SCROLLING CURSOR 时,可以配合以下移动 Cursor 的指令:
在这里插入图片描述
不同 FETCH 指令下 CURSOR 动作示意图:
在这里插入图片描述

LET l_sql = "SELECT * FROM employee_file", "WHERE", l_str  --l_sql是一个字符串

PREPARE emp_pre FROM l_sql --通过preapre宣告成一个可以执行的sql语句

DECLARE emp_cus SCROLL CURSOR 
[WITH HOLD] FOR emp_pre     --将这个sql定义游标为emp_cus

OPEN emp_cus # OPEN CURSOR  --开启游标emp_cus

FETCH FIRST emp_cus INTO l_emp.*  --抓取第一笔资料写入l_emp这个变量中

CLOSE 概述
语法:

CLOSE cursor_id

说明:关闭并释放指标(CURSOR)的储存空间


综合示例

DATABASE ds #声明数据库

MAIN
	DEFINE a STRING
	DEFINE b,c CHAR(10)
	
	# 使用 SQL语句 声明 CURSOR
	DECLARE test01 SCROLL CURSOR FOR 
			SELECT zz01 FROM zz_file WHERE zz01 = "axmt410"
	OPEN test01
	FETCH FIRST test01 INTO b #将cursor指向符合条件的第一笔
	DISPLAY b
	
	LET c = "axmt410"
	LET a = "SELECT zz01 FROM zz_file WHERE zz01='",c CLIPPED,"' "
	# 使用 STRING语句 声明 CURSOR
	DECLARE test02 SCROLL CURSOR FROM a
	OPEN test02
	FETCH LAST test02 INTO b #将cursor指向符合条件的最后一笔
	DISPLAY b

END MAIN

Non-SCROLLING CURSOR(DECLARE、FOREACH)

说明:常运用在 报表程式,抓取资料的方式是 依序 (seguential) 的方式。

  1. 通过 DECLARE 定义
  2. 利用 FOREACH 指令抓资料

DECLARE 概述
语法1:使用 已知的SQL语句 进行 CURSOR 声明

DECLARE cursor_name CURSOR [WITH HOLD] 
	FOR sql_statement

语法2:使用 已声明的 PREPARED_ID 来进行 CURSOR 声明

DECLARE cursor_name CURSOR [WITH HOLD] 
	FOR prepared_id

语法3:使用 已知的STRING语句 进行 CURSOR 声明

DECLARE cursor_name CURSOR [WITH HOLD] 
	FROM string-expression

FOREACH (LOOP) 概述
注:只能用于 Non_Scrolling Cursor

声明完 CURSOR 后即可用 FOREACH 进行将所有符合条件的数据一笔一笔的抓取出来处理,不需要 OPEN。

FOREACHFETCH 的不同

  • FOREACH 具有循环处理的架构,而 FETCH 则必须配合 WHILE 循环一起用。
  • FOREACH 只能循序处理,而 FETCH 可做随机跳跃的选取。
  • 执行 FETCH 前必须 OPEN,结束时 CLOSE 关闭并释放CURSOR
    FOREACH 指令可自动开启与关闭 CURSOR,无需手动 OPEN 和 CLOSE。

综合示例

LET l_sql = "SELECT * FROM employee_file", "WHERE", l_str 
PREPARE emp_pre FROM l_sql 
DECLARE emp_cus CURSOR FOR emp_pre -- 使用 prepare_id 来声明 cursor
FOREACH emp_cus INTO l_emp.* --循序抓资料 
	IF l_emp.salary > 80000 THEN 
		EXIT FOREACH 
	END IF 
END FOREACH
MAIN
	DEFINE clist ARRAY[200] OF RECORD
			cnum INTEGER,
			cname CHAR(50)
		END RECORD
	
	DEFINE I INTEGER
	DEFINE str STRING
	DEFINE c_name CHAR(50)
	
	DATABASE stores
	
	LET c_name = ARG_VAL(1)
	
	LET str = "SELECT customer_num, cust_name FROM customer WHERE cname = ?"
	PREPARE prepare_id FROM str
	DECLARE c1 CURSOR FOR prepare_id -- 使用 prepare_id 来声明 cursor
	LET i = 1
	FOREACH c1 USING c_name INTO clist[i].*
		LET i = i + 1
	END FOREACH
	
	DISPLAY "Number of rows found: ", i
END MAIN

数据的锁定

LOCKING CURSOR(DECLARE、OPEN、FETCH、CLOSE)

当要进行数据的修改 (UPDATE) 时,为防止多人同时修到同一笔数据,应当考虑在开始修改前进行 数据锁定(LOCK),以确保同时间只有一人能取得修改权,Genero FGL 中延续 INFORMIX 4GL 的作法,采用 LOCKING CURSOR 对数据进行锁定。

LOCKING CURSOR 也称 FOR UPDATE CURSOR。用于在数据更新程序段,将数据进行一个上锁的动作,以避免两组以上的联机同时再更新同一 TABLE 下的同一笔数据。如果未作 LOCK 的动作,可能再抓取数据的同时,有其他人正在进行数据修改。此 CURSOR 不属于数据查询的 CURSOR,而需列为更新的 CURSOR。

DECLARE cursor_name CURSOR FOR sql statement FOR UPDATE [NOWAIT]
OPEN cursor_id [USING value]
FETCH cursor_id INTO variable
CLOSE cursor_id

说明:通常运用在 Update程序,做资料Lock的动作。

  1. 通过 DECLARE 定义
  2. 通过 OPEN 开启该CURSOR
  3. 通过 FETCH cursor_name INTO 变量 抓取资料

DECLARE 概述

DECLARE cursor_name CURSOR FOR select_statement FOR UPDATE [NOWAIT]
  • 此处与 SCROLL CURSOR 或 Non-SCROLL CURSOR 最大的差异在于 SQL 查询指令的
    最后须加上FOR UPDATE(ORACLE 数据库需再加上 NOWAIT),以标明此 CURSOR 为 LOCKING CURSOR。
  • 此处亦可使用 FROM char_variable 方式来定义 SQL 查询指令。

OPEN 概述

OPEN cursor_id

FETCH 概述

FETCH cursor_id INTO program_variable
  • 此指令除从数据库中取得数据外,在 LOCKING CURSOR 的状态下,还会将所抓取到的数据锁住,直到程序执行 CLOSE cursor_id 的指令才会释放。

CLOSE 概述

CLOSE cursor_id
  • 关闭并释放 CURSOR,待释放完成后,系统才会将被锁定的数据释放

综合案例

LET l_sql = "SELECT * FROM employee_file", 
			"WHERE no = ? FOR UPDATE" 
PREPARE emp_pre FROM l_sql 
DECLARE emp_cl CURSOR FOR emp_pre 
OPEN emp_cus USING l_no 
FETCH emp_cus INTO l_emp.*  #只抓一笔资料lock
DATABASE ds

MAIN
	 DEFINE g_gav01 LIKE gav_file.gav01
	 DEFINE g_gav08 LIKE gav_file.gav08
	 
	 LET g_forupd_sql = "SELECT * from gav_file WHERE gav01=? AND gav08=? ",
	 					"FOR UPDATE"
	 DECLARE p_per_lock_u CURSOR FROM g_forupd_sql
	 OPEN p_per_lock_u USING g_gav01,g_gav08
	 IF STATUS THEN
		 CLOSE p_per_lock_u
		 RETURN
	 END IF
	 FETCH p_per_lock_u INTO g_gav_lock.*
	 IF SQLCA.sqlcode THEN
		 CLOSE p_per_lock_u
		 RETURN
	 END IF
	 CLOSE p_per_lock_u
END MAIN

USING的使用时机

此处的『USING』和之前关于 变量格式输出的 USING 意义不同。

在组成查询的 SQL 指令时,有时一开始不知道要查询的数据是什么 ,因此『可在 SQL 查询指令中使用问号 ?,后续要使用 CURSOR 时,再将已知值用 USING 传入』。

说明

  • SQL 语句中可以定义多个问号?;使用 USING 给值时需依序对应,并用逗号隔开
  • USING 必需跟在 OPEN 后 (Scroll Cursor 及 Locking Cursor种)、FOREACH 后 (Nonscroll Cursor) 或 EXECUTE 指令后 (大量执行 SQL Cursor)

前面的案例中都用到了 USING。

TRANSACTION:事务控制

说明:需要多表格的同时连动时,可采用 TRANSACTION 作法。

  1. 通过 BEGIN WORK 声明 TRANSACTION 开始(T100写法:s_transaction_begin())
  2. 执行 INSERT、UPDATE、DELETE 等指令
  3. 通过 COMMIT WORK ( T100写法:s_transaction_end('Y','0') )ROLLBACK WORK ( T100写法:s_transaction_end('N','0') ) 声明 TRANSACTION 结束

注意

  • BEGIN WORK 开始后,一定要有 COMMIT WORKROLLBACK WORK也就是必须做出数据是否写入的判断
  • TRANSACTION 区中的程序尽量不要太长,以免影响需要调用同笔数据的其他用户
    (但使用者本身不受影响,未 COMMIT WORK 前仍可调用已变更数据)
  • 有些 DDL 指令 (如 CREATE TABLE) 会有自动 COMMIT WORK 功能
  • COMMIT WORKROLLBACK WORK 时,会自动关闭未声明『WITH HOLD』的 CURSOR
BEGIN WORK
...
[INSERT ...] 
[UPDATE ...] 
...
IF SQLCA.SQLCODE THEN --SQL执行结果
	ROLLBACK WORK 
ELSE 
	COMMIT WORK 
END IF

EXECUTE:大量执行同一SQL指令

当系统要执行大量的同一 SQL 指令,例如要在工作数据中连续 INSERT 十年的工作日数据,可以用 FOR 循环实现以下语句,但数据量大时效率会比较低。

INSERT INTO work_date VALUES ("2005/07/01", "Friday", "Weekday")

若对程序有效率要求,则可以考虑改用『EXECUTE』作法。

说明:需要大量执行同一SQL指令。

  • EXECUTE 可以应用在 SELECT、UPDATE、INSERT、DELETE 等处
  • 使用 EXECUTE 时,尽量将KEY值或其他需要变异的值,保留至EXECUTE指令时再以USING方式传入值
--1. 通过 PREPARE prepared_id FROM sql_statement 宣告SQL指令
--2. EXECUTE prepared_id  [USING variable_list ]  [ INTO fvar [,...] ] 
--3. FREE prepared_id

--语法:
PREPARE prepared_id FROM sql_statement
EXECUTE prepared_id [USING variable_list ] [ INTO fvar [,...] ]
FREE prepared_id


FUNCTION update_customer_name( key, name )
	DEFINE key INTEGER
	DEFINE name CHAR(10)
	PREPARE s1 FROM "UPDATE customer SET name=? WHERE customer_num=?"
	EXECUTE s1 USING name, key
	FREE s1
END FUNCTION

PUT…FLUSH:大量执行新增指令

若是需要大量 INSERT 操作,『INSERT CURSOR』比 EXECUTE 更快。
在这里插入图片描述
示例:

MAIN
	DEFINE i INTEGER
	DEFINE rec RECORD
			key INTEGER,
			name CHAR(30)
		END RECORD
	
	DATABASE stock
	
	PREPARE is FROM "INSERT INTO item VALUES (?,?)"
	DECLARE ic CURSOR FOR is
	
	BEGIN WORK # 开启事务
		OPEN ic
		FOR i=1 TO 100
			LET rec.key = i
			LET rec.name = "Item #" || i
			PUT ic FROM rec.*
			IF i MOD 50 = 0 THEN
				FLUSH ic
			END IF
		END FOR
		CLOSE ic
	COMMIT WORK # 提交事务
	
	FREE ic
	FREE is
END MAIN

ARRAY

ARRAY的定义和方法

Genero 的数组可以设定为一维,二维或多维数组,其计数从『1』开始。若未知数组所需要的长度,可以宣告为动态数组。

固定数组(Static Array静态数组)定义

DEFINE ARRAY [ intconst [,intconst [,intconst] ] ] OF datatype

动态数组(Dynamic Array)定义

DEFINE DYNAMIC ARRAY [ WITH DIMENSION rank ] OF datatype

数组对象可用方法(Method)列表
在这里插入图片描述
示例:定义固定及动态数组

MAIN
	DEFINE a1 ARRAY[100] OF INTEGER --定义容量为100的固定数组
	DEFINE a2 DYNAMIC ARRAY OF INTEGER --定义动态数组
	DEFINE i INTEGER
	LET i = 12
	LET a1[50] = 12456
	LET a2[5000] = 12456
	LET a2[500+i] = 12456
END MAIN


--EX:静态数组(Static Array)
       MAIN
            DEFINE g_desc ARRAY[3] OF CHAR(14)    
            DEFINE g_x      ARRAY[10] OF RECORD
                              a CHAR(10),
                              b CHAR(10),
                              c CHAR(10)
            END RECORD
        END MAIN

--EX:动态数组(Dynamic Array)
       MAIN
            DEFINE g_i DYNAMIC ARRAY OF INTEGER
              DEFINE g_x DYNAMIC  ARRAY  OF RECORD
                                       a CHAR(10),
                                       b CHAR(10),
                                       c CHAR(10)
               END RECORD
        END MAIN

示例:getLength() 和 clear()

MAIN
	DEFINE a DYNAMIC ARRAY OF INTEGER
	LET a[5000] = 123456
	DISPLAY a.getLength() -- 5000
	CALL a.clear() --清空数组
	DISPLAY a.getLength() -- 0
END MAIN

示例:appendElement()

MAIN
	DEFINE a DYNAMIC ARRAY OF INTEGER
	LET a[10] = 10
	CALL a.appendElement()
	LET a[a.getLength()] = a.getLength()
	DISPLAY a.getLength() -- 11
	DISPLAY a[10] -- 10
	DISPLAY a[11] -- 11
END MAIN

示例:insertElement(n) 在下标n处新增记录,并将n之后的数据后移

MAIN
	DEFINE a DYNAMIC ARRAY OF INTEGER
	LET a[10] = 11
	
	CALL a.insertElement(10) --a[10]新增记录,原本a[10]的数据后移
	LET a[10] = 10 --给新增的记录赋值
	
	DISPLAY a.getLength() -- 11
	DISPLAY a[10] -- 10
	DISPLAY a[11] -- 11
END MAIN

范例5:deleteElement(n) 删除下标为n处的记录

MAIN
	DEFINE a DYNAMIC ARRAY OF INTEGER
	LET a[10] = 9
	DISPLAY a.getLength() # 10
	CALL a.deleteElement(5)
	DISPLAY a.getLength() # 9
	DISPLAY a[9] # 9
END MAIN

示例:二、三维数组

MAIN
	DEFINE a2 DYNAMIC ARRAY WITH DIMENSION 2 OF INTEGER
	DEFINE a3 DYNAMIC ARRAY WITH DIMENSION 3 OF INTEGER
	LET a2[50,100] = 12456
	LET a2[51,1000] = 12456
	DISPLAY a2.getLength()
	DISPLAY a2[50].getLength()
	DISPLAY a2[51].getLength()
	LET a3[50,100,100] = 12456
	LET a3[51,101,1000] = 12456
	DISPLAY a3.getLength()
	DISPLAY a3[50].getLength()
	DISPLAY a3[51].getLength()
	DISPLAY a3[50,100].getLength()
	DISPLAY a3[51,101].getLength()
	CALL a3[50].insertElement(10)
	CALL a3[50,10].insertElement(1)
END MAIN

程序中常见的静态数组定义:主要用于单头,搭配executefetch使用

DEFINE p_employee RECORD
	no		LIKE employee_t.no,
	name	LIKE employee_t.name
	tel		LIKE employee_t.tel,
	address	LIKE employee_t.address,
	salary 	LIKE employee_t.salary
END RECORD

INITIALIZE p_employee.* TO NULL -- 静态数组的初始化

程序中常见的动态数组定义:主要用于单身查询报表,搭配foreach使用

DEFINE p_employee DYNAMIC ARRAY OF RECORD
	no		LIKE employee_t.no,
	name	LIKE employee_t.name
	tel		LIKE employee_t.tel,
	address	LIKE employee_t.address,
	salary 	LIKE employee_t.salary
END RECORD

CALL p_employee.clear() # 动态数组的初始化

显示数组内容 DISPLAY ARRAY

在这里插入图片描述
语法:

DISPLAY ARRAY record-array TO screen-array.* 
	[ HELP help-number ] 
	[ATTRIBUTE ( [,...] )

示例:

# 将4gl里面的全局变量(动态数组)显示到屏幕变量上
DISPLAY ARRAY p_employee.* TO s_employee.*

数组内容输入 INPUT ARRAY

  • BEFORE INPUT
    在进入input之前要做的需求设定
    例如将单身的栏位设置为不可录入

  • AFTER INPUT

  • BEFORE ROW
    在进入单身的某行之前,现有开启事务,判断当前是修改还是新增的状态,如果是修改的状态,需要将单身的资料锁住不让其他人修改

  • AFTER ROW

  • BEFORE FIELD field-list

  • AFTER FIELD field-list

  • ON ROW CHANGE
    当单身的某一行发生改变时,会update单身表的资料

  • ON CHANGE field-list

  • ON IDLE idle-seconds

  • ON ACTION action-name

  • BEFORE INSERT
    如果当前的状态是新增,这里是给单身的栏位做预设值,例如:项次自增,复选框默认为N…

  • AFTER INSERT
    将在单身输入的值判断,是否已经存在单身表,如果不存在就新增一笔数据到表,如果已经存在就修改表的资料

  • BEFORE DELETE
    在删除单身数据时要做的逻辑判断

  • AFTER DELETE

  • END INPUT

控制段执行顺序

在这里插入图片描述

多重对话框DIALOG的应用

--语法:
 DIALOG 
       [ATTRIBUTE ( [,...] 
--EX: 
    DIALOG
      DISPLAY ARRAY arr TO srec.*
    END DIALOG
                           
--下列指令可作为DIALOG内层指令使用﹝不限次数﹞:
--INPUT、DISPLAY ARRAY、INPUT ARRAY、CONSTRUCT、SUBDIALOG

DIALOG范例

--EX:
SCHEMA stores 
DEFINE p_customer RECORD LIKE customer.*
DEFINE p_orders DYNAMIC ARRAY OF RECORD LIKE order.*
 
FUNCTION customer_dialog()
   DIALOG ATTRIBUTES(UNBUFFERED, FIELD ORDER FORM)
 
     INPUT BY NAME p_customer.*           # sub-DIALOG
       AFTER FIELD cust_name
         CALL setup_dialog(DIALOG)
     END INPUT
 
     DISPLAY ARRAY p_orders TO s_orders.*   # sub-DIALOG
       BEFORE ROW
         CALL setup_dialog(DIALOG)
     END DISPLAY
 
     ON ACTION close                     # Action for DIALOG
       EXIT DIALOG
 
   END DIALOG
END FUNCTION

 SUBDIALOG:

说明: 当DIALOG内层指令的结构成长到不易维护,或可能会重复于多个DIALOG中使用时,可以将内层指令拆分到独立的4gl档案内进行开发。 开发完成后再使用 IMPORT 与 SUBDIALOG 指令, 将个别开发完成的内层指令組合到原有的DIALOG內。

SUBDIALOG范例 :

--内层指令4gl范例:

SCHEMA stock
DEFINE arr DYNAMIC ARRAY OF RECORD LIKE orders.*

DIALOG orders_dlg()       --DIALOG指令若後方有 id,則視為與FUNCTION相同
    DEFINE x INT
    DISPLAY ARRAY arr TO sr_orders.*
       ...
    END DISPLAY 
END DIALOG

--DIALOG程式結合範例:
IMPORT FGL orders      --使用IMPORT指令將42m於編譯時讀入
SCHEMA stock
DEFINE rec RECORD LIKE customers.*
FUNCTION co_list()
  DIALOG ...
      INPUT BY NAME rec.*
        ...
      END INPUT
        ...
      SUBDIALOG orders.orders_dlg     --使用SUBDIALOG指令引入
        ...
  END DIALOG
END FUNCTION 

REPORT的主要三个指令 

START REPORT report_name[TO {SCREEN|FILE filename|PRINTER}]
说明:
1. 这个指令是在驱动REPORT DRIVER。
2. TO SCREEN:为系统DEFAULT 可不写。
3. TO FILE filenme:将REPORT的结果送到一个档案中(档名自定义)。
4. TO PRINTER:将REPORT结果送到打印机(系统打印机)。
5. 若(2)、(3)都不写,则系统会将结果自动送到屏幕输出。

OUTPUT TO REPORT report_name(variable_list)
说明:该功能类似CALL function,并传递参数,将数据送到REPORT FUNCTION。

FINISH REPORT report_name
说明:此指令为结束报表印制的指令 

报表范例

MAIN
   DEFINE  p_employee RECORD LIKE employee.*
   DECLARE emp1_curs CURSOR FOR
        SELECT * FROM emplyee
   START REPORT employee_report
   FOREACH emp1_curs INTO p_emplyee.*
           OUTPUT TO REPORT employee_report(p_employee.*)
   END FOREACH
   FINISH REPORT employee_report
END MAIN

REPORT employee_report(r_employee)
     DEFINE r_employee RECORD LIKE employee.*
     FORMAT
         ON EVERY ROW
END REPORT

REPORT FUNCTION的组成 

REPORT report_name(expr_list)
    
    [DEFINE define_statement]
 
    [OUTPUT output_statement]
 
    [ORDER BY sort_list]
 
    FORMAT
        control_block
        statement
           :
           :
END REPORT

1.report_name 即是REPORT的名称,与START REPORT rep_name的rep_name是一致的。expr_list为先前主程序所传递的参数
2.OUTPUT Section 为定义报表的边界,报表长度

ORDER BY Section  

语法: ORDER [ EXTERNAL ] BY variable-list

说明:

1. ORDER BY 区间,主要是作排序字段用,以逗号分开,摆在最前面的字段为主键,同时只能用所接收的参数内的字段来排序。
2. ORDER BY 会先将数据排序过并存在暂存盘中,配合BEFORE GROUP OF 或 AFTER GROUP OF区段排序后再由暂存档印出。
3. 加上EXTERNAL即表示传入的数据已经经过外部(START REPORT时)的排序,不存在暂存档中
4. 若后续于FORMAT中有使用GROUP的分群输出功能,则一定要有ORDER BY宣告,必要时可搭配上述EXTRRNAL功能 

FORMAT Section 

这个部份为REPORT FUNCTION的重心,所以报表的排版均由此部份控制。

FIRST PAGE HEADER :报表第一页的表头控制段。
PAGE HEADER :报表每一页的表头控制段。
BEFORE GROUP OF :设定在一组数据的开始之前所必须执行的叙述。
ON EVERY ROW :指定每一笔记录的输出格式。
AFTER GROUP OF :在控制区段设定一组数据之后必须执行的叙述。
PAGE TRAILER :每一页报表页尾的控制段。
ON LAST ROW :所有数据印完后要做的动作,例如”总计”。 

 

其他报表指令 

CLIPPED 将字符串后面的空白清掉。
USING 针对数值定其打印的格式。
COLUMN 指定输出的行位置。
LINENO 取得目前打印行的列号值。
PAGENO 取得目前的页数。
SPACES 传回空白。
TIME 传回系统时间,格式:”hh:mm:ss” TODAY 传回系统今天的日期。
LENGTH(expr) expr为一字符串变量,其会传回expr的长度。

 

posted @ 2022-05-21 11:59  滔天蟹  阅读(2259)  评论(0编辑  收藏  举报