tencent cloud

云开发 CloudBase

SQL 模板

Download
聚焦模式
字号
最后更新时间: 2026-05-13 11:39:02

SQL 模板是什么

SQL 模板为开发者提供了在 MySQL 类型的数据模型上,直接运行 SQL 命令的方式。通过 SQL 模板,可以更容易通过 SQL 命令查询、操作数据库;在数据模型方法不满足复杂查询、多表联表查询等的情况下,可以通过 SQL 模板来解决这类问题。
另外,SQL 模板本身在运行时由于限定了参数,同时默认带有权限条件,因此可以在小程序端、Web 端也发起调用,相比直接的 SQL 命令,适用场景更多,使用更便利。

使用方式

当前 SQL 模板与数据模型关联,相关 SQL 操作的主表即为所关联数据模型对应的表。
在数据模型的管理页面,可以进入 SQL 模板页面,创建及管理 SQL 模板。

创建

在创建 SQL 模板时,需要填写模板名称及 SQL 命令,也可以填写描述用于记录模板相关信息。
模板名称:支持英文、数字及下划线,模板名称将用于后续调用,需要保证全局唯一。
SQL 命令:支持 SELECT、INSERT INTO、UPDATE、DELETE 和 REPLACE INTO 命令。
描述:用于记录描述 SQL 模板相关内容。

测试

在创建时,或创建后,均可以测试运行 SQL 模板。在测试运行时,需要根据 SQL 命令中的参数格式,填写测试用的参数值。
SQL 语句中的自定义参数均需要填写入参后才可运行。
测试时选择 SQL 执行的环境,体验数据及正式数据:如果您的环境支持区分体验及正式数据,在测试时将可以选择在体验数据上测试或在正式数据上测试;测试将直接操作及影响到数据库表中的数据,请您谨慎选择运行。
测试运行后,可以通过结果页面,查看到实际运行结果输出。

管理

您可以通过 SQL 模板的页面查询到当前模型对应的 SQL 模板,可以针对 SQL 模板进行修改、测试、删除。
修改或删除 SQL 模板可能影响到调用 SQL 模板的页面组件、页面 Query、代码等,请谨慎操作。

调用

通过 SDK 中模型方法即可调用模型下的 SQL 模板。
方法使用前需要进行 SDK 初始化,更多 SDK 详情可以查询 数据模型 SDK
控制台 SQL 模板定义及执行示例:

alt text


-- student_select2 对应 SQL

SELECT *
FROM `{{ model.tableName() }}`
WHERE `start_date` > {{ d_start }} AND `end_date` < {{ d_end }}

SDK 执行对应模板:
const res = await app.models.student.runSQLTemplate({
templateName: 'student_select2',
envType: "pre",
params: {
"d_start": "2025-01-01",
"d_end": "2026-01-01"
}
})
console.log(res)
方法说明:
调用方法:models.< modelName >.runSqlTemplate, 其中 modelName 需要替换为具体的数据模型标识。
参数:templateName,填写为 SQL 模板名称
参数:params ,以 key-value 形式提供变量名与变量值的对应;其中变量名对应为 SQL 模板中写入的参数。变量值则是 SQL 模板执行时自动填入的值。
参数:envType, 体验数据或正式数据操作指示,可选参数,默认值为 prod,可选填:prodpre

SQL 语句

支持命令

当前 SQL 模板中仅支持如下的数据操作命令,不支持表操作、库操作相关命令:
SELECT。
INSERT INTO。
REPLACE INTO。
UPDATE。
DELETE。

约定

一个 SQL 模板中仅可以写入一条 SQL 命令,不支持通过“;“引入多条 SQL 命令。
SQL 模板中的主表必须是当前模型的主表,不允许跨模型访问,但联表时的子表可以是同库下的任意表,如:当前模型标识是 student 时,可以使用 SELECT * FROM student,不能使用 SELECT * FROM user,INSERT 等其他指令同理。
当插入数据时(INSERT / REPLACE), 为了保证数据安全,一定要写入系统字段 owner 和 _openid,可以采用函数表达式赋值(user.userId()、user.openId())

参数

SQL 语句中,支持通过 {{param}} 的方式引入变量参数。变量参数用于在实际调用和运行 SQL 模板时传递变量值。
参数写法使用 {{ }} 符号进行包裹。

内置函数

为了结合系统内生逻辑使用 SQL 模板,系统提供了内置函数,使用户在使用 SQL 模板时可以完成模型部分内生逻辑,如:行权限,数据 id 生成、用户标识等。
函数类别
函数表达式
说明
模型
model.tableName()
获得 SQL 模板所属模型对应的表名,自动识别体验和生产
模型
model.tableName('my_model')
获得指定模型对应的表名
模型
model.dataId()
自动生成系统风格的数据 ID
用户
user.userId()
在运行时,获取当前登录人的用户标识
用户
user.openId()
在运行时,获取当前登录人的 openId,如果是小程序登录用户,则对应微信 openId
权限
auth.rowPermission()
在运行时,获取当前登录人对应角色的模型行权限,模型为 SQL 模板绑定模型
权限
auth.rowPermission('my_model')
在运行时,获取当前登录人对应角色的模型行权限,模型由用户指定
系统
system.currentEpoch()
获取当前 10 位秒级时间戳
系统
system.currentEpochMillis()
获取当前 13 位毫秒级时间戳
以上是平台提供的系统级函数,除此以外还可以选择使用 MySQL 原生函数,参考 MYSQL 内置函数

模型函数

model.tableName()
model.tableName('my_model_name')
说明:
获得模型对应的表名,自动识别体验和生产。
当 SQL 模板所属模型标识是 xyz 时,model.tableName() 等价于 model.tableName('xyz')。
背景
1. MySQL 类型的数据模型,底层使用 MySQL 表作为存储载体,在数据模型演进中,模型对应表名的命名规则有版本差异,使用者可以在“云开发平台 > 数据库 > 模型 > 基础信息”中查看模型表名,在 SQL 中直接使用,也可以不关心表名规则,通过函数方式直接操作模型表。
2. 数据模型有 staging 概念,即体验环境和生产环境,两个环境数据是表维度隔离的,不同环境使用不同的表,通过以上函数可以动态的根据执行环境选择对应的表,使用者可以只关心执行环境类型而不用关心环境下模型表名的映射关系,系统自动完成。
示例
-- 直接使用表名
SELECT *
FROM `student-preview`;

-- 使用 SQL 模板绑定的当前模型数据
SELECT *
FROM {{ model.tableName() }};

-- 使用模型student和class联表数据,当前绑定student模型
SELECT student.*, class.*
FROM {{ model.tableName() }} as student
INNER JOIN {{ model.tableName('class_info') }} as class
ON class.student_id = student._id;

model.dataId()
说明:自动生成系统风格的数据 ID。
背景:每个模型都有主键标识,命名为 "_id"(系统字段),用户在操作模型创建数据时,系统会自动为"_id"分配一个字符串主键,是全局唯一的标识,在 SQL 模板中写入数据时,可以手动指定数据标识,也可以使用此函数交给系统自动创建。
示例
-- 手动写入数据标识
INSERT INTO `my_model_table` (`_id`, `name`, `age`)
VALUES ( 'xyz', '张三', 18);

-- SELECT * FROM `my_model_table`
-- ('xyz', '张三', 18)

-- 函数写入数据标识
INSERT INTO `my_model_table` (`_id`, `name`, `age`)
VALUES ( {{ model.dataId() }}, '张三', 18);

-- SELECT * FROM `my_model_table`
-- ('B3PVJN4U2C', '张三', 18)


权限函数

auth.rowPermission()
auth.rowPermission('my_model_name')
说明:
在运行时,获取当前登录人对应角色的模型行权限,可以指定模型的行权限,也可以默认使用 SQL 模板所属的模型行权限。
当 SQL 模板所属模型标识是 xyz 时,auth.rowPermission() 等价于 auth.rowPermission('xyz')
背景:模型支持根据登录人角色设置读写行权限,用来约束登录人对该模型的访问行为。
示例:先设置合理的模型权限,再通过 SQL 模板使用行权限。

模型权限


读权限。

-- 单表读权限
SELECT *
FROM 'student'
WHERE `price` > 100 AND {{ auth.rowPermission() }}
-- auth.rowPermission() 表示默认模型'student'的行权限


-- 联表读权限
SELECT *
FROM 'model1_table' AS table1
LEFT JOIN 'model2_table' AS table2
ON table2.relate_id = table1._id AND {{ auth.rowPermission('model2') }}
WHERE table1.`price` > 100 AND {{ auth.rowPermission() }}
-- auth.rowPermission() 表示默认模型'model1'的行权限
-- auth.rowPermission('model2') 表示指定模型'model2'的行权限

写权限。
-- 仅删除当前登录人有写权限的数据
DELETE FROM 'student'
WHERE `age` > 10 AND {{ auth.rowPermission() }}

-- 仅更新当前登录人有写权限的数据
UPDATE 'student'
SET `name` = '张三'
WHERE {{ auth.rowPermission() }}


用户函数

user.userId()
user.openId()
说明:获取当前登录人的用户标识,userId 和 openId 都是用户标识,用在不同场景中。通俗而言:
1. 当登录用户来自小程序时,openId 表示小程序中的 openId,openId 与 userId 不同,userId 表示系统用户标识。
2. 当登录用户来自非小程序时,多数场景下 userId 和 openId 同值,表示系统用户标识。
3. 在模型的系统字段中,owner、createBy、updateBy 数据来自 user.userId(), _openid 数据来自 user.openId()
示例

-- 约定:插入数据时写入owner和_openId
INSERT INTO 'my_model_table' (`_id`,`name`,`age`, `owner`, `_openid`)
VALUES ('xyz', '张三', 18, {{ user.userId() }}, {{ user.openId() }})


系统函数

system.currentEpoch()
system.currentEpochMillis()
说明:返回 10 位秒级时间戳和 13 位毫秒级时间戳。
背景:在 MySQL 内置函数中,表示 10 位秒级时间戳为 UNIX_TIMESTAMP(NOW()) ,表示 13 位毫秒级时间戳戳 UNIX_TIMESTAMPNOW(3)) * 1000
在系统中,多数情况下都使用 13 位毫秒级时间戳,模型系统字段 createdAt、updatedAt 采用该方式存储。为了简化操作,可以使用系统函数自动完成。
示例

UPDATE 'student'
SET `name` = '张三', `updatedAt` = {{ system.currentEpochMillis() }}
WHERE `_id` = 'xyz';


最佳实践

推荐用法

1. 表名使用函数表达式:如果使用 SQL 模板归属模型的表,使用函数 model.tableName() 表示;如果使用当前环境下其他的表(联表),使用函数 model.tableName('your_model_name')

SELECT * FROM {{ model.tableName() }} as t1
LEFT JOIN {{ model.tableName('other_model_name') }} as t2
ON t2.relate_id = t1._id;

2. 创建数据时:
允许手动给主键 _id 赋值,推荐使用函数赋值 model.dataId()
必填系统字段 owner、_openid ,使用函数 user.userId()user.openId()赋值。
根据业务需要,选填系统字段 createBy、updateBy(使用函数 user.userId())createdAt、updatedAt(使用函数 system.currentEpochMillis())。

INSERT INTO {{ model.tableName() }}
(`_id`, `owner`, `_openid`, `createBy`, `updateBy`, `createdAt`, `updatedAt`)
VALUES
({{ model.dataId() }}, {{ user.userId() }}, {{ user.openId() }}, {{ user.userId() }}, {{ user.userId() }}, {{ system.currentEpochMillis() }}, {{ system.currentEpochMillis() }})

3. 更新、删除时,记录修改时间 updatedAt,使用函数 system.currentEpochMillis()

UPDATE 'student'
SET `name` = '张三', `updatedAt` = {{ system.currentEpochMillis() }}
WHERE `_id` = 'xyz';

4. 读写数据带行权限,使用变量 auth.rowPermission() 或者 auth.rowPermission('model_name')
写权限仅约束更新和删除,创建不受约束。

-- 写权限
UPDATE 'student'
SET `name` = '张三'
WHERE `_id` = {{ id }} AND {{ auth.rowPermission() }}


-- 读权限
SELECT * FROM {{ model.tableName() }}
WHERE `_id` = {{ id }} AND {{ auth.rowPermission() }}


案例分享

接下来,我们结合具体场景,分享如何使用 SQL 模板进行数据库操作。
假设现在有以下模型:
模型标识:teacher_info
字段
说明
类型
系统字段
name
教师姓名
文本
age
年龄
数字
is_head_teacher
是否班主任
布尔
gender
性别
文本
class_id
所属班级
文本
owner
所有人
关联关系
_openid
记录创建者
文本
createBy
创建人
关联关系
updateBy
修改人
关联关系
createdAt
创建时间
日期时间
updatedAt
更新时间
日期时间

创建数据

新建教师信息

定义 SQL 模板, 模板标识: insert_teacher_info_01。

INSERT INTO {{ model.tableName() }}
(`_id`, `name`, `age`, `is_head_teacher`, `gender`, `owner`, `_openid`, `createBy`, `updateBy`, `createdAt`, `updatedAt`)
VALUES
({{ model.dataId() }}, {{ name }}, {{ age }}, {{ is_head_teacher }}, {{ gender }}, {{ user.userId() }}, {{ user.openId() }}, {{ user.userId() }}, {{ user.userId() }}, {{ system.currentEpochMillis() }}, {{ system.currentEpochMillis() }})


SDK 调用

使用模型标识 teacher_info,SQL 模板标识 insert_teacher_info_01,提供自定义变量入参。
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'insert_teacher_info_01',
envType: "pre",
params: {
"name": "张老师",
"age": 30,
"is_head_teacher": true,
"gender": "男"
}
})


创建或更新数据

更新教师信息,如果教师不存在则新建 (依据主键或唯一键是否存在,选择新建或更新)
注意:
如果主键或唯一字段存在时,MySQL 执行机制是先删除再创建。
定义 SQL 模板, 模板标识: replace_teacher_info_01。

REPLACE INTO {{ model.tableName() }}
(`_id`, `name`, `age`, `is_head_teacher`, `gender`, `owner`, `_openid`, `createBy`, `updateBy`, `createdAt`, `updatedAt`)
VALUES
({{ id }}, {{ name }}, {{ age }}, {{ is_head_teacher }}, {{ gender }}, {{ user.userId() }}, {{ user.openId() }}, {{ user.userId() }}, {{ user.userId() }}, {{ system.currentEpochMillis() }}, {{ system.currentEpochMillis() }})


SDK 调用

使用模型标识 teacher_info,SQL 模板标识 replace_teacher_info_01,提供自定义变量入参。
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'replace_teacher_info_01',
envType: "pre",
params: {
"id": "123456", // 如果 id 数据存在则更新,不存在则新建
"name": "张老师",
"age": 28,
"is_head_teacher": false,
"gender": "男"
}
})


语法

当插入数据入参过多时,可以使用语法糖简化 SQL 模板入参定义,通过单一变量数组类型完成。
定义 SQL 模板, 模板标识: insert_teacher_info_02

INSERT INTO {{ model.tableName() }}
(`_id`, `name`, `age`, `is_head_teacher`, `gender`, `owner`, `_openid`, `createBy`, `updateBy`, `createdAt`, `updatedAt`)
VALUES
({{ model.dataId() }}, {{ myParam }}, {{ user.userId() }}, {{ user.openId() }}, {{ user.userId() }}, {{ user.userId() }}, {{ system.currentEpochMillis() }}, {{ system.currentEpochMillis() }})

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'insert_teacher_info_02',
envType: "pre",
params: {
"myParam": ["王老师", 31, true, "女"]
}
})
注意: 语法糖入参,数组的长度必须与 SQL 模板中字段的数量一致,且按照插入字段顺序依次写入值。
此时,SQL 模板中变量 {{ myParam }} 等价于 {{ name }}, {{ age }}, {{ is_head_teacher }}, {{ gender }}

更新数据

说明:
修改教师信息 (仅当前登录用户有权限时可写)。
定义 SQL 模板, 模板标识: update_teacher_info_01

UPDATE {{ model.tableName() }}
SET `name` = {{ name }}, `age` = {{ age }}, `updatedAt` = {{ system.currentEpochMillis() }}
WHERE `_id` = {{ id }} AND {{ auth.rowPermission() }}

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'update_teacher_info_01',
envType: "pre",
params: {
"name": "李老师",
"age": 30,
"id": "123456"
}
})

删除数据

说明:
删除教师信息 (仅当前登录用户有权限时可写)。
定义 SQL 模板, 模板标识: delete_teacher_info_01

DELETE FROM {{ model.tableName() }}
WHERE `_id` = {{ id }} AND {{ auth.rowPermission() }}

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'delete_teacher_info_01',
envType: "pre",
params: {
"id": "123456"
}
})

单表查询

说明:
通过主键 _id 查询单条数据(仅当前登录用户有权限时可见)。
定义 SQL 模板, 模板标识: get_teacher_info_01

SELECT *
FROM {{ model.tableName() }}
WHERE `_id` = {{ id }} AND {{ auth.rowPermission() }}

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'get_teacher_info_01',
envType: "pre",
params: {
"id": "123456"
}
})
说明:
查询所有年龄大于 20 岁的教师信息(仅当前登录用户有权限时可见)。
定义 SQL 模板, 模板标识: get_teacher_info_02

SELECT *
FROM {{ model.tableName() }}
WHERE `age` > {{ age }} AND {{ auth.rowPermission() }}

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'get_teacher_info_02',
envType: "pre",
params: {
"age": 20
}
})

联表查询

说明:
模糊查询张姓的教师信息,并关联查询其对应的班级信息(仅当前登录用户有权限时可见)。
定义 SQL 模板, 模板标识: get_teacher_info_03

SELECT teacher.* , class.*
FROM {{ model.tableName() }} AS teacher
LEFT JOIN {{ model.tableName('class_info') }} AS class
ON teacher.class_id = class._id
WHERE teacher.`name` LIKE {{ var_name }} AND {{ auth.rowPermission() }}
ORDER BY teacher.`age` DESC
LIMIT 20 OFFSET 0

SDK 调用
const res = await app.models.teacher_info.runSQLTemplate({
templateName: 'get_teacher_info_03',
envType: "pre",
params: {
"var_name": "张%"
}
})

补充

在当前场景下,无法使用 MySQL 关键字 "IS" 和 "IS NOT",可以用以下语法替代。
-- 原始语法:不支持
select * from `your_table` WHERE your_column IS NULL
-- 替代语法:支持
select * from `your_table` WHERE your_column <=> NULL


-- 原始语法:不支持
select * from `your_table` WHERE your_column IS NOT NULL
-- 替代语法:支持
select * from `your_table` WHERE !(your_column <=> NULL)



帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈