官方 ODAG 脚本注释

官方原版脚本

官方原版脚本内涵 Load 语法和 SQL 语法两种代码,只需要使用其中一种即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
//不修改这个子程序
SUB ExtendQVDWhere(Name, ValVarName)
LET T = Name & '_COLNAME';
LET ColName = $(T);
LET Values = $(ValVarName);
IF (len(Values) > 0) THEN
IF len(WHERE_PART) > 0 THEN
LET WHERE_PART = '$(WHERE_PART) AND mixmatch([$(ColName)],$(Values) )';
ELSE
LET WHERE_PART = ' WHERE mixmatch([$(ColName)],$(Values))';
ENDIF
ENDIF
END SUB;
/**
* sub 定义子例程
* 自变量将复制到子例程,
* 而如果 call 语句中相应实际参数是变量名,
* 则退出子例程时这些参数将再次从现有子例程中复制回来。
* 如果子例程通过 call 语句调用的形式参数比实际参数多,
* 额外的形式参数将初始化为 NULL 值,且可在子例程中用作局部变量。
* 由于 sub 语句是控制语句,并以分号或行尾结束,两个可能子句( sub 和 end sub) 中任意一个子句都不得跨越行边界。
* call 控制语句可调用必须由先前的 sub 语句定义的子例程。
*
* 关于sub,可以理解为函数,或者是sql里的过程
* 调用时,使用call(),并传入相应的参数
*/
/**
* mixmatch 函数用于将第一个参数与所有以下参数进行比较,并返回匹配表达式的数量。
* 比较不区分大小写。
* 语法:mixmatch( str, expr1 [ , expr2,...exprN ])
*
* 示例 :
* mixmatch( M, 'Jan','Feb','Mar')
* 结果:
* 如果 M = jan,返回 1
*
* ExtendQVDWhere子例程使用Qlik的“mixmatch”函数
* 构建了一个WHERE子句,测试入站记录是否符合条件。
*/
// 不修改这个子程序
SUB ExtendSQLWhere(Name, ValVarName)
LET T = Name & '_COLNAME';
LET ColName = $(T);
LET Values = $(ValVarName);
IF (len(Values) > 0) THEN
IF len(WHERE_PART) > 0 THEN
LET WHERE_PART = '$(WHERE_PART) AND $(ColName) IN ( $(Values) )';
ELSE
LET WHERE_PART = ' WHERE $(ColName) IN ( $(Values) )';
ENDIF
ENDIF
END SUB;

/**
* OdagService默认使用单引号包装每个值,并在每个值之间插入逗号
*
* BuildValueList子例程负责用逗号分隔值并将引号括起来
* 在需要它的情况下传递BuildValueList的第4个参数为39,会使它将每个值用single包装引号,
* 传递0则不用单引号包装每个值,这是因为SQL中的数值,不需要引号引用数字。
*
* chr函数,返回值是一个字符串,返回指定的字符代码相关的字符 。
* chr(39)返回单引号,chr(44)返回逗号
*/
//不修改这个子程序
SUB BuildValueList(VarName, TableName, ColName, QuoteChrNum)
IF ($(QuoteChrNum) = 0) THEN
LET LOADEXPR = 'Concat($(ColName),' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ELSE
LET CHREXPR = ' chr(' & '$(QuoteChrNum)' & ') ';
LET LOADEXPR = 'Concat( $(CHREXPR) & $(ColName) & $(CHREXPR)' & ',' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ENDIF
_TempTable:
LOAD $(LOADEXPR) Resident $(TableName);
Let vNoOfRows = NoOfRows('_TempTable');
IF $(vNoOfRows)> 0 THEN
LET $(VarName) = Peek('CombinedData',0,'_TempTable');
ENDIF
drop table _TempTable;
drop table '$(TableName)';
END SUB;


/* 如果任何选择字段的数据库列名具有不同的名称,则更新这些内联表加载块,使其来自您的选择应用程序。
* 内嵌load语句记录体中的$()中的内容必须在其用户"Selection App"中的字段名称匹配。
* 如果所选字段的数据库列名有所不同,需要更改SET xxxx_COLNAME=''语句的右侧来反映这一点
*
* 所有ODAG的字段都以od为前缀,下面的字段表示所选择的或相关的值
*
* ods = Selected values 所选值
* odo = Associated values 关联值
* odso = Selected/associated values 所选值/关联值
* od = ods = Selected values 所选值
*
* 对于ODAG绑定的内联表形式,使用“quote”和“delimiter”选项来禁止OdagService的默认行为
* 我们希望将值作为一个仅用换行分隔的文字列表注入内联表。
*/

/**
* 每定义一个表字段 OdageBinding 就调用一次 BuidValueList子例程
* 以下定义了6个表字段,反复调用了6次子例程
* 由于每次声明完就直接调用,所以没有修改表名,不存在覆盖变量名导致出现BUG的问题
*/
/**
* 注意:
* 一个子例程应当字段均来自一个表中
* 如果需要在其他表字段创建选择项
*
* 方法1:
* 将所需表字段合成一个新表(官方案例即是这个思路)
*
* 方法2:
* 创建多个子线程,何所需要对应的for循环
*/
SET ORIGIN='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Origin Code){"quote": "", "delimiter": ""}
];
SET ORIGIN_COLNAME='Origin Code';

//SET ORIGIN_COLNAME = 'ORIGIN'; // SQL版本
CALL BuildValueList('ORIGIN', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET DEST='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Destination Code){"quote": "", "delimiter": ""}
];
SET DEST_COLNAME='Destination Code';
// SET DEST_COLNAME='DEST'; // SQL version
CALL BuildValueList('DEST', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET YEAR='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Year){"quote": "", "delimiter": ""}
];
SET YEAR_COLNAME='Year';
// SET YEAR_COLNAME='YEAR'; // SQL version
CALL BuildValueList('YEAR', 'OdagBinding', 'VAL', 0); //0表示没有对值进行包装,因为年份是数值型的

SET QUARTER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Quarter){"quote": "", "delimiter": ""}
];
SET QUARTER_COLNAME='Quarter';
// SET QUARTER_COLNAME='QUARTER'; // SQL version
CALL BuildValueList('QUARTER', 'OdagBinding', 'VAL', 0); //0表示不包装值,因为四分之一是数值

SET TICKET_CARRIER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Ticket Carrier Code){"quote": "", "delimiter": ""}
];
SET TICKET_CARRIER_COLNAME = 'Ticket Carrier Code';
// SET TICKET_CARRIER_COLNAME = 'TICKET_CARRIER'; // SQL version
CALL BuildValueList('TICKET_CARRIER', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET FARE_CLASS='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Fare Class){"quote": "", "delimiter": ""}
];
SET FARE_CLASS_COLNAME='Fare Class';
// SET FARE_CLASS_COLNAME='FARE_CLASS'; // SQL version
CALL BuildValueList('FARE_CLASS', 'OdagBinding', 'VAL', 39); // 39 is for single quote wrapping values
/**
* LOAD * INLINE []
* 手动创建表,手动添加表数据
* VAL 是字段名,
* $(odso_%){} 是字段值
*
* //声明一个变量,存储字段
* SET ORIGIN='';
*
* //odso_ 后面对应load字段名
* $(odso_Origin Code){"quote": "", "delimiter": ""}
*
* //COLNAME前面是变量名
* //对应的值是load字段名
* SET ORIGIN_COLNAME='Origin Code';
*
* //第一个参数,是变量
* CALL BuildValueList('ORIGIN','','','');
*/

SET WHERE_PART = '';

/**
* For循环中 fldname IN 的值是:上面声明的变量名,他们存储的是ods字段数据
* 更改下面每个列表中的字段名列表,以反映以下字段的名称在上面的内联绑定中绑定到Selection App 。
*/
FOR EACH fldname IN 'ORIGIN', 'DEST', 'YEAR', 'QUARTER', 'TICKET_CARRIER', 'FARE_CLASS'
LET vallist = $(fldname);
IF (IsNull(vallist)) THEN
LET vallist = '';
ENDIF
IF (len(vallist) > 0) THEN
CALL ExtendQVDWhere('$(fldname)','vallist');
// CALL ExtendSQLWhere('$(fldname)','vallist'); // use this version for SQL
ENDIF
NEXT fldname

/**
* trace 语句用于将字符串写入脚本执行进度窗口和脚本日志文件中。这对调试过程很有用。
* 使用在trace 语句之前计算的 $ 变量扩展可以自定义消息。
*
* 语法:
* Trace string
*
* 示例 1:
* Trace Main table loaded;
*
* 示例 2:
* Let MyMessage = NoOfRows('MainTable') & ' rows in Main Table';
* Trace $(MyMessage);
*/
TRACE Generated WHERE clause: ;
TRACE $(WHERE_PART);

//创建指向QVD位置的文件夹连接
LET FOLDER='lib://odag_apps';
/**
*Exists() 用于确定是否已经将特定字段值加载到数据加载脚本中的字段。
*此函数用于返回 TRUE 或FALSE,
*这样它可以用于 LOAD 语句或 IF 语句中的 where 子句。
*语法:Exists(field_name [, expr])
*/
LET FLIGHTS_QVD='[$(FOLDER)/FlightsUnder100k.qvd] (qvd)';
LET AIRLINES_QVD='[$(FOLDER)/AirlinesUnder100k.qvd] (qvd) WHERE Exists("Ticket Carrier Code","Ticket Carrier Code")';
LET ORIGINS_QVD='[$(FOLDER)/OriginAirportsUnder100k.qvd] (qvd) WHERE Exists("Origin Code","Origin Code")';
LET DESTS_QVD='[$(FOLDER)/DestAirportsUnder100k.qvd] (qvd) WHERE Exists("Destination Code","Destination Code")' ;
LET FARES_QVD='[$(FOLDER)/FaresUnder100k.qvd] (qvd) WHERE Exists("Fare Class","Fare Class")';

/**
* 注意:
* Exists函数可以忽略,
* 除了作为关联表的主表以外,可以直接加载表数据
*/

// QVD version:
LOAD *
FROM $(FLIGHTS_QVD)
$(WHERE_PART);

// SQL version:
// Flights:
// LOAD "FARE_CLASS" as "Fare Class",
// "ORIGIN_STATE_ABR" as "Origin State",
// "DEST_STATE_ABR" as "Destination State",
// "QUARTER" as "Quarter",
// "ORIGIN" as "Origin Code",
// "DEST" as "Destination Code",
// "TICKET_CARRIER" as "Ticket Carrier Code",
// "YEAR" as "Year",
// "FLIGHT_COUNT",
// "PASSENGERS",
// "DISTANCE",
// "MKT_ID";
// SQL SELECT
// "MKT_ID",
// "YEAR",
// "QUARTER",
// "ORIGIN",
// "ORIGIN_STATE_ABR",
// "DEST",
// "DEST_STATE_ABR",
// "TICKET_CARRIER",
// "FARE_CLASS",
// "PASSENGERS",
// "DISTANCE",
// 1 AS "FLIGHT_COUNT"
// FROM SAPH7T."/QT/AIRPORT_FACT"
// $(WHERE_PART);


//用维度提取脚本替换
// QVD version:
Airlines:
LOAD *
FROM $(AIRLINES_QVD);

// SQL version:
// Airlines:
// LOAD TICKET_CARRIER as "Ticket Carrier Code",
// "Description" as Airline
// WHERE Exists("Ticket Carrier Code","TICKET_CARRIER");
// SQL SELECT
// "TICKET_CARRIER",
// "Description"
// FROM "SAPH7T"."/QT/CARRIERS";

// QVD version:
OriginAirports:
LOAD *
FROM $(ORIGINS_QVD);

// SQL version:
// OriginAirports:
// LOAD "Code" as "Origin Code",
// "Description" as "Origin Name"
// WHERE Exists("Origin Code","Code");
// SQL SELECT "Code",
// "Description"
// FROM "SAPH7T"."/QT/AIRPORT_CODE";

// QVD version:
DestAirports:
LOAD *
FROM $(DESTS_QVD);

// SQL version:
// DestAirports:
// LOAD "Code" as "Destination Code",
// "Description" as "Destination Name"
// WHERE Exists("Destination Code","Code");
// SQL SELECT "Code",
// "Description"
// FROM "SAPH7T"."/QT/AIRPORT_CODE";

// QVD version:
Fares:
LOAD *
FROM $(FARES_QVD);

// SQL version:
// Fares:
// LOAD FARE_CLASS as "Fare Class",
// "Fare_Class_Description" as "Fare Class Name"
// WHERE Exists("Fare Class","FARE_CLASS");
// SQL SELECT "FARE_CLASS",
// "Fare_Class_Description"
// FROM "SAPH7T"."/QT/FARE_CLASS";


//更改# 7(可选):
//此可选更改是添加应用程序可能需要的任何附加固定数据加载或脚本代码。

QVD 版本

独立出 QVD 版本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/**
* sub 定义子例程
* 自变量将复制到子例程,
* 由于 sub 语句是控制语句,并以分号或行尾结束。
* call 控制语句可调用必须由先前的 sub 语句定义的子例程。
*
* Sub可以理解为PL/SQL中的 过程
* 通过 Call() 语句调用
* 如果 call 语句调用的形参比实参多,
* 额外的形参将初始化为 NULL 值,且可在子例程中用作局部变量
*/
//不修改这个子程序
SUB ExtendQVDWhere(Name, ValVarName)
LET T = Name & '_COLNAME';
LET ColName = $(T);
LET Values = $(ValVarName);
IF (len(Values) > 0) THEN
IF len(WHERE_PART) > 0 THEN
LET WHERE_PART = '$(WHERE_PART) AND mixmatch([$(ColName)],$(Values) )';
ELSE
LET WHERE_PART = ' WHERE mixmatch([$(ColName)],$(Values))';
ENDIF
ENDIF
END SUB;
/**
* mixmatch 函数用于将第一个参数与所有以下参数进行比较,并返回匹配表达式的数量。
* 比较不区分大小写。
* 语法:mixmatch( str, expr1 [ , expr2,...exprN ])
*
* ExtendQVDWhere子例程使用Qlik的“mixmatch”函数
* 构建了一个WHERE子句,测试入站记录是否符合条件。
*/

/**
* OdagService默认使用单引号包装每个值,并在每个值之间插入逗号
*
* BuildValueList子例程负责用逗号分隔值并将引号括起来
* 在需要它的情况下传递BuildValueList的第4个参数为39,会使它将每个值用single包装引号,
* 传递0则不用单引号包装每个值,这是因为SQL中的数值,不需要引号引用数字。
*
* chr函数,返回值是一个字符串,返回指定的字符代码相关的字符 。
* chr(39)返回单引号,chr(44)返回逗号
*/
//不修改这个子程序
SUB BuildValueList(VarName, TableName, ColName, QuoteChrNum)
IF ($(QuoteChrNum) = 0) THEN
LET LOADEXPR = 'Concat($(ColName),' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ELSE
LET CHREXPR = ' chr(' & '$(QuoteChrNum)' & ') ';
LET LOADEXPR = 'Concat( $(CHREXPR) & $(ColName) & $(CHREXPR)' & ',' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ENDIF
_TempTable:
LOAD $(LOADEXPR) Resident $(TableName);
Let vNoOfRows = NoOfRows('_TempTable');
IF $(vNoOfRows)> 0 THEN
LET $(VarName) = Peek('CombinedData',0,'_TempTable');
ENDIF
drop table _TempTable;
drop table '$(TableName)';
END SUB;

/**
* 所有ODAG的字段都以od为前缀,下面的字段表示所选择的或相关的值
*
* ods = Selected values 所选值
* odo = Associated values 关联值
* odso = Selected/associated values 所选值/关联值
* od = ods = Selected values 所选值
*
* 对于ODAG绑定的内联表形式,
* 使用“quote”和“delimiter”来禁止OdagService的默认行为
* 我们希望将值作为一个仅用换行分隔的文字列表注入内联表。
*/

SET ORIGIN='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Origin Code){"quote": "", "delimiter": ""}
];
SET ORIGIN_COLNAME='Origin Code';
CALL BuildValueList('ORIGIN', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET DEST='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Destination Code){"quote": "", "delimiter": ""}
];
SET DEST_COLNAME='Destination Code';
CALL BuildValueList('DEST', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET YEAR='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Year){"quote": "", "delimiter": ""}
];
SET YEAR_COLNAME='Year';
CALL BuildValueList('YEAR', 'OdagBinding', 'VAL', 0); //0表示没有对值进行包装

SET QUARTER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Quarter){"quote": "", "delimiter": ""}
];
SET QUARTER_COLNAME='Quarter';
CALL BuildValueList('QUARTER', 'OdagBinding', 'VAL', 0); //0表示没有对值进行包装

SET TICKET_CARRIER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Ticket Carrier Code){"quote": "", "delimiter": ""}
];
SET TICKET_CARRIER_COLNAME = 'Ticket Carrier Code';
CALL BuildValueList('TICKET_CARRIER', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET FARE_CLASS='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Fare Class){"quote": "", "delimiter": ""}
];
SET FARE_CLASS_COLNAME='Fare Class';
CALL BuildValueList('FARE_CLASS', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

/**
* LOAD * INLINE []
* 手动创建表,手动添加表数据
* VAL 是字段名,
* $(odso_%){} 是字段值
*
* //声明一个变量,存储字段
* SET ORIGIN='';
*
* //odso_ 后面对应load字段名
* $(odso_Origin Code){"quote": "", "delimiter": ""}
*
* //COLNAME前面是变量名
* //对应的值是load字段名
* SET ORIGIN_COLNAME='Origin Code';
*
* //第一个参数,是变量
* CALL BuildValueList('ORIGIN','','','');
*/

SET WHERE_PART = '';

/**
* For循环中 fldname IN 的值是:上面声明的变量名,他们存储的是ods字段数据
*/

FOR EACH fldname IN 'ORIGIN', 'DEST', 'YEAR', 'QUARTER', 'TICKET_CARRIER', 'FARE_CLASS'
LET vallist = $(fldname);
IF (IsNull(vallist)) THEN
LET vallist = '';
ENDIF
IF (len(vallist) > 0) THEN
CALL ExtendQVDWhere('$(fldname)','vallist');
ENDIF
NEXT fldname

/**
* trace 语句用于将字符串写入脚本执行进度窗口和脚本日志文件中,这对调试过程很有用。
* 使用在trace 语句之前计算的 $ 变量扩展可以自定义消息。
*
* 语法:
* Trace string
*/
TRACE Generated WHERE clause: ;
TRACE $(WHERE_PART);

//创建指向QVD文件夹的连接
LET FOLDER='lib://odag_apps';
/**
*Exists() 用于确定是否已经将特定字段值加载到数据加载脚本中的字段。
*此函数用于返回 TRUE 或FALSE,
*这样它可以用于 LOAD 语句或 IF 语句中的 where 子句。
*语法:Exists(field_name [, expr])
*/
LET FLIGHTS_QVD='[$(FOLDER)/FlightsUnder100k.qvd] (qvd)';
LET AIRLINES_QVD='[$(FOLDER)/AirlinesUnder100k.qvd] (qvd) WHERE Exists("Ticket Carrier Code","Ticket Carrier Code")';
LET ORIGINS_QVD='[$(FOLDER)/OriginAirportsUnder100k.qvd] (qvd) WHERE Exists("Origin Code","Origin Code")';
LET DESTS_QVD='[$(FOLDER)/DestAirportsUnder100k.qvd] (qvd) WHERE Exists("Destination Code","Destination Code")' ;
LET FARES_QVD='[$(FOLDER)/FaresUnder100k.qvd] (qvd) WHERE Exists("Fare Class","Fare Class")';

/**
* 注意:
* Exists函数可以忽略,
* 除了作为关联表的主表以外,可以直接加载表数据
*/

LOAD *
FROM $(FLIGHTS_QVD)
$(WHERE_PART);

Airlines:
LOAD *
FROM $(AIRLINES_QVD);

OriginAirports:
LOAD *
FROM $(ORIGINS_QVD);

DestAirports:
LOAD *
FROM $(DESTS_QVD);

Fares:
LOAD *
FROM $(FARES_QVD);

SQL 版本

独立出 SQL 版本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/**
* sub 定义子例程
* 自变量将复制到子例程,
* 由于 sub 语句是控制语句,并以分号或行尾结束。
* call 控制语句可调用必须由先前的 sub 语句定义的子例程。
*
* Sub可以理解为PL/SQL中的 过程
* 通过 Call() 语句调用
* 如果 call 语句调用的形参比实参多,
* 额外的形参将初始化为 NULL 值,且可在子例程中用作局部变量
*/

// 不修改这个子程序
SUB ExtendSQLWhere(Name, ValVarName)
LET T = Name & '_COLNAME';
LET ColName = $(T);
LET Values = $(ValVarName);
IF (len(Values) > 0) THEN
IF len(WHERE_PART) > 0 THEN
LET WHERE_PART = '$(WHERE_PART) AND $(ColName) IN ( $(Values) )';
ELSE
LET WHERE_PART = ' WHERE $(ColName) IN ( $(Values) )';
ENDIF
ENDIF
END SUB;

/**
* OdagService默认使用单引号包装每个值,并在每个值之间插入逗号
*
* BuildValueList子例程负责用逗号分隔值并将引号括起来
* 在需要它的情况下传递BuildValueList的第4个参数为39,会使它将每个值用single包装引号,
* 传递0则不用单引号包装每个值,这是因为SQL中的数值,不需要引号引用数字。
*
* chr函数,返回值是一个字符串,返回指定的字符代码相关的字符 。
* chr(39)返回单引号,chr(44)返回逗号
*/
//不修改这个子程序
SUB BuildValueList(VarName, TableName, ColName, QuoteChrNum)
IF ($(QuoteChrNum) = 0) THEN
LET LOADEXPR = 'Concat($(ColName),' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ELSE
LET CHREXPR = ' chr(' & '$(QuoteChrNum)' & ') ';
LET LOADEXPR = 'Concat( $(CHREXPR) & $(ColName) & $(CHREXPR)' & ',' & chr(39) & ',' & chr(39) & ') AS CombinedData';
ENDIF
_TempTable:
LOAD $(LOADEXPR) Resident $(TableName);
Let vNoOfRows = NoOfRows('_TempTable');
IF $(vNoOfRows)> 0 THEN
LET $(VarName) = Peek('CombinedData',0,'_TempTable');
ENDIF
drop table _TempTable;
drop table '$(TableName)';
END SUB;

/**
* 所有ODAG的字段都以od为前缀,下面的字段表示所选择的或相关的值
*
* ods = Selected values 所选值
* odo = Associated values 关联值
* odso = Selected/associated values 所选值/关联值
* od = ods = Selected values 所选值
*
* 对于ODAG绑定的内联表形式,
* 使用“quote”和“delimiter”来禁止OdagService的默认行为
* 我们希望将值作为一个仅用换行分隔的文字列表注入内联表。
*/

SET ORIGIN='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Origin Code){"quote": "", "delimiter": ""}
];
SET ORIGIN_COLNAME = 'ORIGIN';
CALL BuildValueList('ORIGIN', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET DEST='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Destination Code){"quote": "", "delimiter": ""}
];
SET DEST_COLNAME='DEST';
CALL BuildValueList('DEST', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET YEAR='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Year){"quote": "", "delimiter": ""}
];
SET YEAR_COLNAME='YEAR';
CALL BuildValueList('YEAR', 'OdagBinding', 'VAL', 0); //0表示没有对值进行包装,因为年份是数值型的

SET QUARTER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Quarter){"quote": "", "delimiter": ""}
];
SET QUARTER_COLNAME='QUARTER';
CALL BuildValueList('QUARTER', 'OdagBinding', 'VAL', 0); //0表示不包装值,因为四分之一是数值

SET TICKET_CARRIER='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Ticket Carrier Code){"quote": "", "delimiter": ""}
];
SET TICKET_CARRIER_COLNAME = 'TICKET_CARRIER';
CALL BuildValueList('TICKET_CARRIER', 'OdagBinding', 'VAL', 39); // 39是单引号的换行值

SET FARE_CLASS='';
OdagBinding:
LOAD * INLINE [
VAL
$(odso_Fare Class){"quote": "", "delimiter": ""}
];
SET FARE_CLASS_COLNAME='FARE_CLASS';
CALL BuildValueList('FARE_CLASS', 'OdagBinding', 'VAL', 39); //39是单引号的换行值
/**
* LOAD * INLINE []
* 手动创建表,手动添加表数据
* VAL 是字段名,
* $(odso_%){} 是字段值
*
* //声明一个变量,存储字段
* SET ORIGIN='';
*
* //odso_ 后面对应load字段名
* $(odso_Origin Code){"quote": "", "delimiter": ""}
*
* //COLNAME前面是变量名
* //对应的值是SQL字段名(as语句前的字段名)
* SET ORIGIN_COLNAME='ORIGIN';
*
* //第一个参数,是变量
* CALL BuildValueList('ORIGIN','','','');
*/

SET WHERE_PART = '';

/**
* For循环中 fldname IN 的值是:上面声明的变量名,他们存储的是ods字段数据
*/

FOR EACH fldname IN 'ORIGIN', 'DEST', 'YEAR', 'QUARTER', 'TICKET_CARRIER', 'FARE_CLASS'
LET vallist = $(fldname);
IF (IsNull(vallist)) THEN
LET vallist = '';
ENDIF
IF (len(vallist) > 0) THEN
CALL ExtendSQLWhere('$(fldname)','vallist');
ENDIF
NEXT fldname
/**
* trace 语句用于将字符串写入脚本执行进度窗口和脚本日志文件中,这对调试过程很有用。
* 使用在trace 语句之前计算的 $ 变量扩展可以自定义消息。
*
* 语法:
* Trace string
*/
TRACE Generated WHERE clause: ;
TRACE $(WHERE_PART);


LIB CONNECT TO '';

Flights:
LOAD "FARE_CLASS" as "Fare Class",
"ORIGIN_STATE_ABR" as "Origin State",
"DEST_STATE_ABR" as "Destination State",
"QUARTER" as "Quarter",
"ORIGIN" as "Origin Code",
"DEST" as "Destination Code",
"TICKET_CARRIER" as "Ticket Carrier Code",
"YEAR" as "Year",
"FLIGHT_COUNT",
"PASSENGERS",
"DISTANCE",
"MKT_ID";
SQL SELECT
"MKT_ID",
"YEAR",
"QUARTER",
"ORIGIN",
"ORIGIN_STATE_ABR",
"DEST",
"DEST_STATE_ABR",
"TICKET_CARRIER",
"FARE_CLASS",
"PASSENGERS",
"DISTANCE",
1 AS "FLIGHT_COUNT"
FROM SAPH7T."/QT/AIRPORT_FACT"
$(WHERE_PART);

/**
* 注意:
* Exists函数可以忽略,可以直接加载表数据
*/
Airlines:
LOAD TICKET_CARRIER as "Ticket Carrier Code",
"Description" as Airline
WHERE Exists("Ticket Carrier Code","TICKET_CARRIER");
SQL SELECT
"TICKET_CARRIER",
"Description"
FROM "SAPH7T"."/QT/CARRIERS";


OriginAirports:
LOAD "Code" as "Origin Code",
"Description" as "Origin Name"
WHERE Exists("Origin Code","Code");
SQL SELECT "Code",
"Description"
FROM "SAPH7T"."/QT/AIRPORT_CODE";

DestAirports:
LOAD "Code" as "Destination Code",
"Description" as "Destination Name"
WHERE Exists("Destination Code","Code");
SQL SELECT "Code",
"Description"
FROM "SAPH7T"."/QT/AIRPORT_CODE";

Fares:
LOAD FARE_CLASS as "Fare Class",
"Fare_Class_Description" as "Fare Class Name"
WHERE Exists("Fare Class","FARE_CLASS");
SQL SELECT "FARE_CLASS",
"Fare_Class_Description"
FROM "SAPH7T"."/QT/FARE_CLASS";

官方 ODAG 脚本注释
https://blog.pangcy.cn/2019/05/07/数据库相关/QlikSense/官方 ODAG 脚本注释/
作者
子洋
发布于
2019年5月7日
许可协议