Files
playbook/docs/tsl/syntax/14_ts_sql.md
T
cshandClaude Fable 5 014c23d386 🎨 style(syntax): normalize naming conventions
Align all TSL syntax documentation examples with docs/tsl/naming.md:

- Classes/types: PascalCase, drop Hungarian prefix (THuman→Human)
- Parameters/locals: snake_case with meaningful names (isLeft→is_left, maxb→max_b)
- Private members: snake_case_ with trailing underscore (real_part_, imaginary_part_)
- Public members: PascalCase (value→Value)
- Top-level functions: PascalCase (test→Test)
- Module constants: kPascalCase (kernel_dll→kKernelDll)

Affected: 01-24 syntax docs (23 files)
Verified: 150+ code blocks locally tested, output unchanged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:34:19 +08:00

27 KiB
Raw Blame History

TSL TS-SQL

文档类型:语法主线 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;13_resultset_and_filters.md12_matrix_and_collections.md23_fmarray.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md

这一篇是 TS-SQL 的唯一语法入口:内存数组查询、返回形态、字段访问、where / group by / order by、一维数组查询、多表 join(含 left join)、insert / update / delete 写回、thisGroupthisRowIndexrefMaxOf / refMinOf 都在这里收拢。

本篇职责

回答”写 TS-SQL 查询和写回时,怎样从最小 select ... from ... end 骨架开始,逐步处理筛选、分组、排序、多表联接(含 LEFT JOIN)、组内子查询、极值引用,以及如何用 insert/update/delete 修改内存数组”。

智能体 TS-SQL 判断流程

  1. 先判断要写查询(select/sselect/vselect/mselect)还是写回(insert/update/delete)。
  2. 内存数组查询优先从 select ... from source_rows end 最小骨架起手。
  3. 二维结果集字段访问用 ["字段名"];多表查询字段访问用 [表序号].["字段名"]
  4. 在一维数组上做 TS-SQL 时,优先使用 thisRowthisRowIndex
  5. 联接选 join / left join / right join / full join / cross join / 逗号联接;等值联接可用 with(... on ...) 优化。
  6. 聚集统一形态 (Expr, Cond, N, MovingFirst, CacheId):条件聚集传 Cond,移动聚集传 N;分组后筛选用 having;自定义聚集用 aggof
  7. 只想按已有结果集保留/排除行时跳到 13_resultset_and_filters.md;要做去重型集合关系时跳到 12_matrix_and_collections.md;要在 FMArray 上做查询或写回边界时跳到 23_fmarray.md
  8. 没有对应代码块时不要发明 TS-SQL 写法。

核心规则

  • TS-SQL 是 TSL 自带的类 SQL 查询语法,不是金融业务函数库。
  • 基础查询文档骨架是:以 select / sselect / vselect / mselect 开始,以 end 收尾。
  • from 后面可以直接跟内存数组结果集。
  • 在内存二维结果集上,文档字段访问写法是 ["字段名"]
  • 在一维数组上做 TS-SQL 时,优先使用 thisRowthisRowIndex
  • select 返回二维结果,sselect 返回一维结果,vselect 返回单值,mselect 返回 Matrix
  • wheregroup byorder by 可以直接接在 from 后面继续使用;order by 支持 asc/desc 与多列逗号分隔。
  • 分组后按聚集条件筛选用 havingwhere 不能用聚集);having 里用 countof([字段])countof(1),不要用 countof(*)
  • 多表 join 时,字段访问应写成 [表序号].["字段名"]on 可用 and 写多条件;[表序号].* 取整表列。
  • 联接类型:left join 保留左表、right join 保留右表、full join 保留双方、cross join 笛卡尔积、逗号联接等价于 cross join;不匹配处用 nil 填充。
  • select 列表支持 distinct 去重、as 别名as nil(参与计算但不返回)、起始列 to 结束列 字段区间、selectopt(位选项)drange(区间/M of N)
  • 聚集函数统一形态 Func(Expr[, Cond[, N[, MovingFirst[, CacheId]]]]):条件聚集、移动聚集、多字段聚集、refof(Expr, N) 引用相对行;aggof('名', Expr) 调用自定义聚集回调。
  • thisGroup 不是普通值,而是分组后的子结果集;要通过子 select / vselectfrom thisGroup 来访问。
  • thisRowIndexorder by 之后仍可返回原始行位置;thisOrder 返回排序后的自然排名。
  • refMaxOf(...)refMinOf(...) 可与 maxOf(...) / minOf(...) 配合,取极值所在行的另一列值。
  • [@字段] 返回该字段的数据类型字符串。
  • 写回语句 insert / update / delete 直接修改原内存数组,不返回新数组。
  • 访问金融表、时间序列或业务数据源时,语法和业务语义要分开看;本页只讲语言层查询骨架。

可直接照写示例

最小查询骨架

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1)
);
query_result := select * from source_rows end;
writeLn(length(query_result));

结果说明:

  • query_result 的长度是 2
  • 两行依次是 (1,3)(2,1)
  • 说明 TS-SQL 的最短可靠入口就是“准备结果集,然后 select ... from source_rows end

代码块身份:输出片段

2

字段选择

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1),
    ("A": 1, "B": 2)
);
query_result := select ["A"], ["B"] from source_rows end;
writeLn(length(query_result));

结果说明:

  • query_result 的长度是 3
  • 三行依次是 (1,3)(2,1)(1,2)
  • 说明 select ["A"], ["B"] from source_rows end 会按原顺序返回二维结果集

四个查询入口怎样分工

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1),
    ("A": 1, "B": 2)
);
selected_values := sselect ["A"] from source_rows end;
sum_value := vselect sumOf(["B"]) from source_rows end;
matrix_result := mselect * from source_rows end;
col_index := mcols(matrix_result, 1);

结果说明:

  • sselect ["A"] from source_rows end 返回一维数组 array(1, 2, 1)
  • vselect sumOf(["B"]) from source_rows end 返回单值 6
  • mselect * from source_rows end 的行数是 3、列数是 2
  • mcols(matrix_result, 1) 返回列索引 array("A", "B")
  • 本页只把 mselect 的“返回 Matrix 且保留行列信息”写成文档主干;不要在本页发明直接单元格读取规则

whereorder by

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1),
    ("A": 1, "B": 2)
);
query_result := select * from source_rows where ["B"] > 1 order by ["B"] end;

结果说明:

  • query_result 的长度是 2
  • 两行依次是 (1,2)(1,3)
  • 说明 where ["B"] > 1 会先筛选,再按 order by ["B"] 的升序返回

group by

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1),
    ("A": 1, "B": 2)
);
group_result := select ["A"], sumOf(["B"]) as "SumB"
                from source_rows
                group by ["A"]
                order by ["A"]
                end;

结果说明:

  • group_result 的长度是 2
  • 第一行是 (1,5)
  • 第二行是 (2,1)
  • 说明 group by ["A"] 后可以直接接聚集函数,并用 as "SumB" 指定返回列名

一维数组上的 thisRowthisRowIndex

代码块身份:可直接照写示例

values := array(10, 20, 30);
row_values := sselect thisRow from values end;
row_indexes := sselect thisRowIndex from values end;
query_result := select thisRow as "Value", thisRowIndex as "Idx"
                from values
                where thisRow > 15
                order by thisRow
                end;

结果说明:

  • sselect thisRow from values end 返回 array(10, 20, 30)
  • sselect thisRowIndex from values end 返回 array(0, 1, 2)
  • 上面的 select ... from values where thisRow > 15 order by thisRow end 返回两行:第一行 Value=20, Idx=1,第二行 Value=30, Idx=2

join

代码块身份:可直接照写示例

left_rows := array(
    ("ID": 1, "V1": 10),
    ("ID": 2, "V1": 20)
);
right_rows := array(
    ("ID": 1, "V2": 100),
    ("ID": 3, "V2": 300)
);
join_result := select [1].["ID"], [1].["V1"], [2].["V2"]
               from left_rows join right_rows on [1].["ID"] = [2].["ID"]
               end;
writeLn(length(join_result));
writeLn(join_result[0]["ID"]);
writeLn(join_result[0]["V1"]);
writeLn(join_result[0]["V2"]);

结果说明:

  • join_result 的长度是 1
  • 唯一一行是 (1,10,100)
  • 说明 from left_rows join right_rows on ...[1].["字段"][2].["字段"] 这种多表字段访问属于文档明确写法

代码块身份:输出片段

1
1
10
100

thisGroup

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3, "Name": "x"),
    ("A": 2, "B": 1, "Name": "y"),
    ("A": 1, "B": 2, "Name": "z")
);
group_result := select ["A"], max_b := maxOf(["B"]) as "MaxB",
                vselect ["Name"] from thisGroup where ["B"] = max_b end as "TopName"
                from source_rows
                group by ["A"]
                order by ["A"]
                end;

结果说明:

  • group_result 的长度是 2
  • 第一行是 (1,3,"x")
  • 第二行是 (2,1,"y")
  • 说明 thisGroup 可以在分组上下文里作为子结果集继续 vselect

thisRowIndex 在排序后仍指向原始位置

代码块身份:可直接照写示例

source_rows := array(
    ("A": 1, "B": 3),
    ("A": 2, "B": 1),
    ("A": 1, "B": 2)
);
query_result := select thisRowIndex as "Idx", ["B"]
                from source_rows
                order by ["B"]
                end;

结果说明:

  • query_result 的三行依次是 (1,1)(2,2)(0,3)
  • 说明 order by ["B"] 之后,thisRowIndex 仍返回原表中的原始下标

refMaxOfrefMinOf

代码块身份:可直接照写示例

source_rows := array((6, 20), (5, 20), (9, 2), (2, 20), (7, 18));
max_ref_result := select maxOf([0]) as "MaxA", refMaxOf([1]) as "RefB" from source_rows end;
min_ref_result := select minOf([0]) as "MinA", refMinOf([1]) as "RefB" from source_rows end;

结果说明:

  • max_ref_result 只有一行,结果是 (9,2)
  • min_ref_result 只有一行,结果是 (2,20)
  • 说明 refMaxOf([1]) 取到了 [0] 最大值所在行的 [1]refMinOf([1]) 取到了 [0] 最小值所在行的 [1]

LEFT JOIN 多表联接

left join 保留左表所有行,右表不匹配时用 nil 填充:

代码块身份:可直接照写示例

left_rows := array(("k": 1, "va": "a1"), ("k": 2, "va": "a2"));
right_rows := array(("k": 1, "vb": "b1"), ("k": 3, "vb": "b3"));
result := select [1].["va"], [2].["vb"]
          from left_rows left join right_rows
          on [1].["k"] = [2].["k"]
          end;
writeLn("行数:", mrows(result));
writeLn("(0,0):", result[0]["va"]);
writeLn("(0,1):", result[0]["vb"]);
writeLn("(1,0):", result[1]["va"]);
writeLn("(1,1):", result[1]["vb"]);

代码块身份:输出片段

行数: 2
(0,0): a1
(0,1): b1
(1,0): a2
(1,1): nil

说明:

  • left join 保留左表所有行(k=1k=2
  • 右表 k=2 不存在,对应列用 nil 填充
  • on 子句指定联接条件,用 [1].["k"] = [2].["k"] 匹配键
  • 其他联接类型:right join(保留右表)、full join(保留双方)、cross join(笛卡尔积)

JOIN 家族其余形态

right join / full join 的不匹配行同样用 nil 填充:

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
b := array(("id": "01", "math": 70), ("id": "05", "math": 50));
right_result := select [1].["id"], [2].["math"] from a right join b on [1].["id"] = [2].["id"] end;
full_result := select [1].["id"], [2].["math"] from a full join b on [1].["id"] = [2].["id"] end;
writeLn("right行数:", mrows(right_result));
writeLn("full行数:", mrows(full_result));

代码块身份:输出片段

right行数: 2
full行数: 3

说明:

  • right join 保留右表所有行,左表无匹配的记录里,取自左表的字段为 nil(上例右表 id=05 那行 [1].["id"]nil
  • full join 保留双方所有行,任一侧无匹配的字段用 nil 填充(上例既有左表独占的 02,也有右表独占的 05

cross join 是笛卡尔积,不带 on;等价的逗号联接可带 where

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
b := array(("id": "01", "math": 70), ("id": "05", "math": 50));
cross_result := select [1].["id"], [2].["id"] from a cross join b end;
comma_result := select [1].["id"] from a, b where [1].["id"] = [2].["id"] end;
writeLn("cross行数:", mrows(cross_result));
writeLn("comma行数:", mrows(comma_result));

代码块身份:输出片段

cross行数: 4
comma行数: 1

说明:

  • cross join 不写 on,产生左右两表的全组合(2 × 2 = 4 行)
  • from A, B where ... 是等价的逗号联接写法,用 where 表达匹配条件

with(... on ...) 是优化联接:把 on 的 N×M 匹配复杂度降到 N+M,多个等值约束用逗号分隔:

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "03", "en": 85));
b := array(("id": "01", "math": 70), ("id": "03", "math": 60), ("id": "05", "math": 50));
result := select [1].["id"], [2].["math"] from a join b with([1].["id"] on [2].["id"]) end;
writeLn("行数:", mrows(result));

代码块身份:输出片段

行数: 2

说明:

  • join B with(左表达式组 on 右表达式组) 只支持等值约束,靠哈希把复杂度降到 N+M
  • 多个等值约束写成 with([1].["id"], [1].["cls"] on [2].["id"], [2].["cls"])
  • 需要非等值条件(><)时回到普通 join ... on

on 支持多条件 and[表序号].* 取整表所有列:

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "03", "en": 85));
b := array(("id": "01", "math": 70), ("id": "03", "math": 95));
result := select [1].* from a join b on [1].["id"] = [2].["id"] and [1].["en"] > [2].["math"] end;
writeLn("行数:", mrows(result));
writeLn("(0,0):", result[0]["id"]);

代码块身份:输出片段

行数: 1
(0,0): 01

说明:

  • on 后面可以用 and 串联多个条件
  • [1].* 返回第一个表的所有列;三表以上时链式写 join C on ...

INSERT 写回

insert into 向内存数组插入新行:

代码块身份:可直接照写示例

a := array(("id": "x"));
insert into a insertfields(["id"]) values("y");
writeLn("行数:", mrows(a));
writeLn("(0,0):", a[0]["id"]);
writeLn("(1,0):", a[1]["id"]);

代码块身份:输出片段

行数: 2
(0,0): x
(1,0): y

说明:

  • insert into 数组变量 直接修改原数组
  • insertfields([字段列表]) 指定要插入的字段
  • values(...) 提供对应值,可以写多组 valuesvalues 实现批量插入

批量插入可以直接跟一个同结构数组,insertfields 也支持一次给多字段赋值:

代码块身份:可直接照写示例

a := array(("id": "01", "cls": "A"));
rows := array(("id": "09", "cls": "C"));
insert into a rows;
insert into a insertfields(["id"], ["cls"]) values("07", "B");
writeLn("行数:", mrows(a));
writeLn(a[1]["id"], a[1]["cls"]);
writeLn(a[2]["id"], a[2]["cls"]);

代码块身份:输出片段

行数: 3
09C
07B

说明:

  • insert into a rows; 把整个 rows 数组的行批量追加进 a
  • insertfields(["id"], ["cls"]) values("07", "B") 一次插入多字段

UPDATE 写回

update 修改符合条件的行:

代码块身份:可直接照写示例

a := array(("id": "x", "v": 1), ("id": "y", "v": 2));
update a set ["v"] = 99 where ["id"] = "y" end;
writeLn(a[0]["v"]);
writeLn(a[1]["v"]);

代码块身份:输出片段

1
99

说明:

  • update 数组变量 set [字段] = 新值 直接修改原数组
  • where 子句筛选要更新的行
  • 可以同时更新多个字段:set ["v1"] = 10, ["v2"] = 20

set 一个不存在的列会自动新增该列;set thisrow = ... 可以整行替换(一维数组上尤其常用):

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
update a set ["total"] = ["en"] + 1 end;
r := array(1, 2, 3);
update r set thisrow = thisrow * 10 end;
writeLn(a[0]["total"]);
writeLn(r[0], r[1], r[2]);

代码块身份:输出片段

91
102030

说明:

  • set ["total"] = ...total 原本不存在,执行后自动作为新列加到每一行
  • set thisrow = thisrow * 10 用当前行整体做表达式并写回;一维数组上 thisrow 就是元素本身

DELETE 写回

delete 删除符合条件的行:

代码块身份:可直接照写示例

a := array(("id": "x"), ("id": "y"));
delete from a where ["id"] = "x";
writeLn("行数:", mrows(a));
writeLn("(0,0):", a[0]["id"]);

代码块身份:输出片段

行数: 1
(0,0): y

说明:

  • delete from 数组变量 where 条件 删除符合条件的行
  • 直接修改原数组
  • 省略 where 会删除所有行

deleteopt(Option) 控制删除后是否重排下标:

代码块身份:可直接照写示例

a := array(("cls": "A"), ("cls": "A"), ("cls": "B"), ("cls": "B"));
delete deleteopt(1) from a where ["cls"] = "A";
writeLn(mrows(a));

代码块身份:输出片段

2

说明:

  • deleteopt(0)(默认)删除后重排剩余行下标
  • deleteopt(1) 删除后保留原始下标,不重排

distinct 结果集去重

select distinct 对结果集去重;聚集函数内也可用 distinct 前缀:

代码块身份:可直接照写示例

a := array(("cls": "A", "en": 90), ("cls": "A", "en": 80), ("cls": "B", "en": 85));
distinct_rows := select distinct ["cls"] from a end;
distinct_sum := vselect sumof(distinct ["en"]) from a end;
writeLn(mrows(distinct_rows));
writeLn(distinct_sum);

代码块身份:输出片段

2
255

说明:

  • select distinct [字段] 折叠重复行
  • sumof(distinct [字段]) 只对不同值求和:90 + 80 + 85 = 255

as 别名、as nil 与字段区间

代码块身份:可直接照写示例

a := array(("id": "01", "cls": "A", "en": 90), ("id": "02", "cls": "B", "en": 80));
alias_rows := select ["en"] as "score" from a end;
nil_rows := select ["id"], ["en"] * 2 as nil from a end;
range_rows := select 0 to 1 from a end;
writeLn(alias_rows[0]["score"]);
writeLn(mcols(nil_rows));
writeLn(mcols(range_rows));

代码块身份:输出片段

90
1
2

说明:

  • 字段 as "别名" 给结果列改名
  • 表达式 as nil 让该列只参与临时计算,不出现在结果集里(上例 nil_rows 只剩 id 一列)
  • StartIndex to EndIndex 在选择列表里取列区间;0 to 1 返回第 0、1 两列

drange 取行区间

代码块身份:可直接照写示例

a := array(("id": "01"), ("id": "02"), ("id": "03"), ("id": "04"));
head_rows := select drange(0 to 1) * from a end;
tail_rows := select drange(-2 to -1) * from a end;
part_rows := select drange(1 of 2) * from a end;
writeLn(mrows(head_rows));
writeLn(tail_rows[0]["id"]);
writeLn(mrows(part_rows));

代码块身份:输出片段

2
03
2

说明:

  • drange(begin to end) 取行区间;负数从末尾计(-1 是最后一行)
  • drange(M of N) 把结果集等分成 N 份,取第 M 份

selectopt 位选项

代码块身份:可直接照写示例

a := array(("en": 90), ("en": 80), ("en": 85), ("en": 85));
opt_rows := select selectopt(2) ["en"] from a end;
writeLn(dataType(opt_rows));
writeLn(opt_rows[0]);

代码块身份:输出片段

4
90

说明:

  • selectopt(N) 用位组合改变返回形态;selectopt(2)select 返回一维数组(不再是二维结果集)
  • 常用位:1=单值、2=一维、4=Matrix、16=多字段聚集保留原名、64=MovingFirst 变换

条件聚集、移动聚集与 refof

聚集函数统一支持 (Expr, BoolConditionExp, N, MovingFirst, CacheId) 形态:

代码块身份:可直接照写示例

a := array(("en": 90), ("en": 80), ("en": 85), ("en": 85));
cond_sum := vselect sumof(["en"], ["en"] >= 85) from a end;
moving := select avgof(["en"], true, 2, true) from a end;
ref_prev := select ["en"], refof(["en"], 1) from a end;
writeLn(cond_sum);
writeLn(moving[2]["Expr1"]);
writeLn(ref_prev[1]["Expr1"]);

代码块身份:输出片段

260
82.5
90

说明:

  • 条件聚集:sumof(表达式, 布尔条件) 只统计条件为真的行(90 + 85 + 85 = 260
  • 移动聚集:avgof(表达式, 条件, N, MovingFirst) 取当前行往前 N 条的滑动统计
  • refof(表达式, N) 引用前 N 行的值(N 为负则往后);首行无前值时返回 0

group by ... having

having 用聚集条件筛选分组(where 不能用聚集):

代码块身份:可直接照写示例

a := array(("cls": "A", "en": 90), ("cls": "A", "en": 80), ("cls": "B", "en": 85));
having_rows := select ["cls"] from a group by ["cls"] having countof(["cls"]) > 1 end;
writeLn(mrows(having_rows));
writeLn(having_rows[0]["cls"]);

代码块身份:输出片段

1
A

说明:

  • having 聚集条件 在分组后筛选(上例只保留成员数大于 1 的 A 组)
  • having 里的计数用 countof([字段])countof(1)

代码块身份:反例 / 不可照写

having_rows := select ["cls"] from a group by ["cls"] having countof(*) > 1 end;

countof(*) 这种带 * 的写法不成立,会报 CountOf ( not found。计数改用 countof([字段])countof(1)

thisOrder 与多列 order by

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80), ("id": "03", "en": 85), ("id": "04", "en": 85));
order_rows := select ["id"], thisOrder as "ord" from a order by ["en"] end;
desc_rows := select ["id"] from a order by ["en"] desc end;
writeLn(order_rows[0]["id"], ",", order_rows[0]["ord"]);
writeLn(desc_rows[0]["id"]);

代码块身份:输出片段

02,1
01

说明:

  • thisOrder 返回排序后的自然排名(从 1 起,值相同则同名次,名次可不连续)
  • order by [字段] desc 降序;多列排序用逗号分隔:order by ["cls"] asc, ["en"] desc
  • thisOrderthisRowIndex 不同:后者返回排序前的原始行位置

refsof 引用上级结果集

在嵌套子查询里,refsof(Exp, UpLevel) 用上 N 级结果集计算 Exp

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
r := select ["id"], (vselect refsof(["en"], 1) from a end) as "up" from a end;
writeLn(r[0]["id"], ",", r[0]["up"]);
writeLn(r[1]["id"], ",", r[1]["up"]);

代码块身份:输出片段

01,90
02,80

说明:

  • refsof(表达式, 1) 在内层子查询里引用上一级(外层)结果集当前行的值
  • 数字越大引用越靠外层;脱离嵌套单独使用会报 no result set for reference

[@Field] 取字段类型

代码块身份:可直接照写示例

a := array(("id": "01", "en": 90), ("id": "02", "en": 80));
t := select ["id"], [@"en"] from a end;
writeLn(t[0]["Expr1"]);

代码块身份:输出片段

integer

说明:

  • [@字段名] 返回该字段的数据类型名(字符串),可用于 select 列表与 update set

aggof 自定义聚集扩展

代码块身份:仅服务端可执行示例 本地 TSL.exe 会报 AggOf Init Error;下例在服务端(pyTSL)验证通过。

aggof('名称', 表达式) 调用一个自定义回调函数做聚集:

代码块身份:仅服务端可执行示例

Table1 := array(("C": 1), ("C": 2), ("C": 3), ("C": 4), ("C": 5));
return vselect aggof('AggSumSample', ['C']) from Table1 end;

function AggSumSample(Flag, Value);
begin
    if Flag = 0 then
    begin
        sysParams['SumSample'] := 0;
        return true;
    end
    else if Flag = 1 then
    begin
        sysParams['SumSample'] := sysParams['SumSample'] + Value;
        return true;
    end
    else
        return sysParams['SumSample'];
end;

说明(上例返回 15):

  • 回调签名固定为 function Name(Flag, Value)
  • Flag=0 初始化(Value 为真表示 distinct),返回 true/false 表示成败
  • Flag=1 每行数据,Value 是当前行表达式的值,返回 true/false
  • Flag=2 结束,返回最终聚集结果
  • 状态用 sysParams[...] 缓存,不要用 static

本页不生成的范围

  • TSQLInsert / TSQLSetValue / TSQLBatchInsert / TSQLEdit / TSQLPost / TSQLFinal 对象被 TS-SQL 查询的回调机制
  • 面向 SQL 表、业务表或时间序列的数据查询与写回(marketTable / infoTable / tradeTable / sqlTable / hugeSqlTable 等数据源)

这些内容不作为本页可生成事实;业务数据源先查 ../codegen/dotnet/datawarehouse/../modules/pytsl_api.md 或项目实际接口。

默认生成模板

TS-SQL 的最短默认骨架如下:

代码块身份:可直接照写示例

query_result := select * from source_rows end;

禁止项

  • 不要把数据库 SQL 方言直接迁移成 TS-SQL 代码。
  • 不要把 select 当成普通函数调用,忘了以 end 收尾。
  • 在二维结果集里直接写 A 而不是 ["A"]
  • 多表联接时继续写成 ["ID"],没有加表序号。
  • 处理一维数组时直接把 [0] 当成稳定列访问。
  • thisGroup 当成普通字段或普通变量。
  • 把排序后的 thisRowIndex 误当成排序序号。
  • left join 时省略 on 子句或不用 [表序号].["字段"] 形式。
  • insert 时漏掉 insertfields 或字段数与值数不匹配。
  • 期望 update/delete 返回新数组;它们直接修改原数组。
  • countof(*) 数行数;* 星号形式不被支持,改用 countof([字段])countof(1)

代码块身份:反例 / 不可照写

n := vselect countof(*) from source_rows end;

countof(*) 会报 CountOf ( not found。数行数改用 countof([字段])countof(1)

代码块身份:反例 / 不可照写

query_result := select A from source_rows end;

上面这种写法不要当成本页可靠规则。二维结果集字段访问,本页只把 ["A"] 这种写法写成文档主干。

代码块身份:反例 / 不可照写

query_result := select [0] from values end;

这种对一维数组直接用 [0] 的写法虽然返回长度为 3 的结果,但取到的值是 nil,不能当成可靠入口。对一维数组应改用 thisRowthisRowIndex

代码块身份:反例 / 不可照写

query_result := select ["ID"], ["V1"], ["V2"]
                from left_rows join right_rows on ["ID"] = ["ID"]
                end;

上面这种写法不要当成本页可靠规则。多表查询里,本页只把 [1].["字段"][2].["字段"] 这种带表序号的访问方式写成文档主干。

代码块身份:反例 / 不可照写

group_value := thisGroup;

不要把 thisGroup 当成普通值直接使用。可靠入口是 select ... from thisGroup endvselect ... from thisGroup end