# 基于宏变量的元编程
元编程(Metaprogramming)指在程序运行时操作或者创建程序的一种编程技术,简而言之就是使用代码编写代码。通过元编程将原本静态的代码通过动态的脚本生成,使程序员可以创建更加灵活的代码以提升编程效率。
在 DolphinDB 中,元编程常用于 SQL 语句的编写。通过 SQL 元编程,可以解决下述 2 个场景的问题:
- 场景一:SQL 的字段名或过滤条件等是动态的,需要通过函数参数或变量进行传递。例如,用户报表系统,由前端用户选择查询字段,后端生成 SQL 查出数据。
- 场景二:查询列数非常多的宽表中的多个字段列,或者对普通表中多个字段执行相似的操作(例如同时对多个字段求和),如果完全通过脚本书写 SQL 语句,脚本冗长且耗时。
本教程将围绕 SQL 元编程展开,通过与传统的函数式元编程对比,介绍 V3.00.0 / 2.00.12 版本支持的更高效简洁的元编程方法——基于宏变量的元编程。
- [1. 基于函数的元编程](#1-基于函数的元编程)
- [2. 基于宏变量(Macro Variables)的元编程](#2-基于宏变量macro-variables的元编程)
- [3. 字段序列(Column Series)在元编程中的应用](#3-字段序列column-series在元编程中的应用)
- [4. 场景案例](#4-场景案例)
- [4.1 计算最小二乘回归的残差,列字段动态传入](#41-计算最小二乘回归的残差列字段动态传入)
- [4.2 多个结构相同的列计算](#42-多个结构相同的列计算)
- [4.3 多列计算返回一个结果列](#43-多列计算返回一个结果列)
- [4.4 where 多条件](#44-where-多条件)
- [4.5 每行记录按存储的计算代码(string)进行计算](#45-每行记录按存储的计算代码string进行计算)
- [5. 总结](#5-总结)
## 1. 基于函数的元编程
基于函数的元编程实现,是指通过内置元编程函数的组合调用生成元代码。以 select 查询语句为例,一个 select 语句通常可以拆分为以下几个部分:select 查询主体,表对象,分组字段,排序字段,过滤条件,返回记录数约束等(参考下图标红部分)。
为了适用于各种 SQL 语句的生成,DolphinDB 设计了用于 SQL 语句的组装的函数 sql(select 语句),sqlUpdate(update 语句),sqlDelete(delete 语句),或者也可以通过 parseExpr + SQL 字符串生成 SQL 元代码。
以 sql 函数为例,其语法为:
```
sql(select, from, [where], [groupBy], [groupFlag], [csort], [ascSort], [having], [orderBy], [ascOrder], [limit], [hint], [exec=false])
```
sql 函数的每个参数都对应 SQL 的一个子句,通过变量传入即可动态生成对应的 SQL 元代码。
例如,上图的语句可以通过下述元编程脚本生成:
```
sel=sqlColAlias(makeUnifiedCall(cumsum, sqlCol("price")), "cum_price")
fm="t"
wre=parseExpr("time between 09:00:00 and 15:00:00")
ctxBy=sqlCol("securityID")
cs=sqlCol("time")
lim=-1
sql(select=sel, from=fm, where=wre, groupby=ctxBy, groupFlag=0, csort=cs, limit=lim)
// output:
< select cumsum(price) as cum_price from objByName("t") where time between pair(09:00:00, 15:00:00) context by securityID csort time asc limit -1 >
```
参考以上例子,可以总结出 sql 函数生成元代码的规则:
- 查询字段须以 sqlCol 或者 sqlColAlias 声明。
- 对表字段计算的元代码:
- 单字段参与计算:sqlCol 函数指定 func 参数。
- 多字段参与计算:sqlColAlias 函数 搭配 makeCall 或者 makeUnifiedCall 函数。
- 表对象可以是一个表变量名字符串、表变量如 t 或 loadTable 返回的句柄。
具体的参数说明请参考:[sql](https://docs.dolphindb.cn/zh/funcs/s/sql.html)
sql 函数生成的元代码是基于一些小的元代码片段组装的,为了进一步理解这个规则,下面介绍一下组装涉及到的相关函数及其作用:
- sqlCol:支持生成单字段或多字段应用同一函数的表达式,支持指定别名;生成的表达式形如:
```
(1) sqlCol("col") -->
(2) sqlCol(["col0","col1","col2"]) --> [, , …, ]
(3) sqlCol("col", func=sum, alias="newCol") -->
(4) sqlCol(["col0","col1","col2"], func=sum, alias=["newCol0","newCol1","newCol2"])
--> [, , ]
```
- sqlColAlias:为复杂的列字段计算元代码指定别名;生成的表达式形如:
```
(1) sqlColAlias(sqlCol("col"), "newCol") -->
(2) sqlColAlias(makeCall(sum, sqlCol("col")), "newCol")
-->
(3) sqlColAlias(makeCall(corr, sqlCol("col0"), sqlCol("col1")), "newCol")
-->
```
通常搭配下述函数使用:
- makeCall, makeUnifiedCall: 用于生成 的元代码表达式。
- expr, unifiedExpr, binaryExpr:生成多元算术表达式,例如 ,
- parseExpr:从字符串生成元代码,将拼接、API 上传或脚本读取的字符串,生成可执行的脚本。例如 parseExpr(“select * from t”) 即可生成