# TSL TS-SQL

文档类型：语法主线
是否可直接用于生成代码：是
是否含可直接照写示例：是
是否含不可照写反例：是
遇到不确定时：先按本页候选页继续判断；[13_resultset_and_filters.md](13_resultset_and_filters.md)、[12_matrix_and_collections.md](12_matrix_and_collections.md)、[23_fmarray.md](23_fmarray.md)；仍不命中时回到语法路由中心 [index.md](index.md)；如果问题已经超出语法层，回到 TSL 总入口 [../index.md](../index.md)

这一篇是 TS-SQL 的唯一语法入口：内存数组查询、返回形态、字段访问、`where` / `group by` / `order by`、一维数组查询、多表 `join`、`thisGroup`、`thisRowIndex`、`refMaxOf` / `refMinOf` 都在这里收拢。

## 本篇职责

回答“写 TS-SQL 查询时，怎样从最小 `select ... from ... end` 骨架开始，逐步处理筛选、分组、排序、多表联接、组内子查询和极值引用”。

## 智能体 TS-SQL 判断流程

1. 先判断要写 `select`、`sselect`、`vselect`、`mselect`，还是 `join` / `thisGroup` / 极值引用。
2. 内存数组查询优先从 `select ... from source_rows end` 最小骨架起手。
3. 二维结果集字段访问用 `["字段名"]`；多表查询字段访问用 `[表序号].["字段名"]`。
4. 在一维数组上做 TS-SQL 时，优先使用 `thisRow` 和 `thisRowIndex`。
5. 只想按已有结果集保留/排除行时跳到 [13_resultset_and_filters.md](13_resultset_and_filters.md)；要做去重型集合关系时跳到 [12_matrix_and_collections.md](12_matrix_and_collections.md)；要在 `FMArray` 上做查询或写回边界时跳到 [23_fmarray.md](23_fmarray.md)。
6. 没有对应代码块时不要发明 TS-SQL 写法。

## 核心规则

- TS-SQL 是 TSL 自带的类 SQL 查询语法，不是金融业务函数库。
- 基础查询文档骨架是：以 `select` / `sselect` / `vselect` / `mselect` 开始，以 `end` 收尾。
- `from` 后面可以直接跟内存数组结果集。
- 在内存二维结果集上，文档字段访问写法是 `["字段名"]`。
- 在一维数组上做 TS-SQL 时，优先使用 `thisRow` 和 `thisRowIndex`。
- `select` 返回二维结果，`sselect` 返回一维结果，`vselect` 返回单值，`mselect` 返回 `Matrix`。
- `where`、`group by`、`order by` 可以直接接在 `from` 后面继续使用。
- 多表 `join` 时，字段访问应写成 `[表序号].["字段名"]`。
- `thisGroup` 不是普通值，而是分组后的子结果集；要通过子 `select` / `vselect` 的 `from thisGroup` 来访问。
- `thisRowIndex` 在 `order by` 之后仍可返回原始行位置，而不是排序后的序号。
- `refMaxOf(...)` 和 `refMinOf(...)` 可与 `maxOf(...)` / `minOf(...)` 配合，取极值所在行的另一列值。
- 访问金融表、时间序列或业务数据源时，语法和业务语义要分开看；本页只讲语言层查询骨架。

## 可直接照写示例

### 最小查询骨架

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

```tsl
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`”

代码块身份：输出片段

```text
2
```

### 字段选择

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

```tsl
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` 会按原顺序返回二维结果集

### 四个查询入口怎样分工

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

```tsl
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 且保留行列信息”写成文档主干；不要在本页发明直接单元格读取规则

### `where` 和 `order by`

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

```tsl
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`

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

```tsl
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"` 指定返回列名

### 一维数组上的 `thisRow` 与 `thisRowIndex`

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

```tsl
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`

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

```tsl
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].["字段"]` 这种多表字段访问属于文档明确写法

代码块身份：输出片段

```text
1
1
10
100
```

### `thisGroup`

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

```tsl
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"], maxb := maxOf(["B"]) as "MaxB",
                vselect ["Name"] from thisGroup where ["B"] = maxb 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` 在排序后仍指向原始位置

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

```tsl
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` 仍返回原表中的原始下标

### `refMaxOf` 与 `refMinOf`

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

```tsl
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]`

## 本页不生成的范围

- `with on`
- `left join` / `right join` / `full join`
- `RefsOf`
- `insert` / `update` / `delete`
- `TSQLInsert` / `TSQLSetValue` / `TSQLBatchInsert`
- `TSQLEdit` / `TSQLPost` / `TSQLFinal`
- 面向 SQL 表、业务表或时间序列的数据查询与写回

这些内容不作为本页可生成事实；业务数据源先查 [../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md)、[../modules/pytsl_api.md](../modules/pytsl_api.md) 或项目实际接口。

## 默认生成模板

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

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

```tsl
query_result := select * from source_rows end;
```

## 禁止项

- 不要把数据库 SQL 方言直接迁移成 TS-SQL 代码。
- 不要把 `select` 当成普通函数调用，忘了以 `end` 收尾。
- 在二维结果集里直接写 `A` 而不是 `["A"]`。
- 多表联接时继续写成 `["ID"]`，没有加表序号。
- 处理一维数组时直接把 `[0]` 当成稳定列访问。
- 把 `thisGroup` 当成普通字段或普通变量。
- 把排序后的 `thisRowIndex` 误当成排序序号。
- 把 `with on`、写回接口和时间序列缓存写进默认模板。

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

```text
query_result := select A from source_rows end;
```

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

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

```text
query_result := select [0] from values end;
```

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

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

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

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

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

```text
group_value := thisGroup;
```

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