-
Notifications
You must be signed in to change notification settings - Fork 0
Affected Rows
写入可以报出它动了多少行,脚本可以拿这个数字做判断。没有事务也能写条件更新,靠的就是它:
update entities in table "accounts" with limit 1 and store affected rows in {_rows}:
values:
balance: {_balance} - {_amount}
where all:
id = {_from}
balance = {_balance}
if {_rows} is 0:
send "读余额的时候它已经被改掉了。" to console
store affected rows in {_rows} 是可选的,每条写入语句都能带:insert one、insert many、insert ... if absent、update、upsert、delete,section 形式和不带冒号的形式都一样。它带着一个 and,写在语句自己的参数之后——那个 and 不是装饰,它让 Skript 知道前面那个参数到哪里为止。它后面写不写 and wait 都照收,也都不起作用。
delete entities from table "logs" with limit 500 and store affected rows in {_deleted}
insert one {_user::*} into table "archived_users" and store affected rows in {_rows}
upsert one entity in table "users" by id {_id} and store affected rows in {_rows}:
values:
name: "Alice"
目标必须是单个变量,写 {_rows} 而不是 {_rows::*}:列表变量会把数字塞进一个没人指定的键里,而表达式根本没法被写入。这两种情况都会在解析脚本时就报错。
它是数据库自己报出的“写入了多少行”:插入加了多少行、删除去掉多少行、更新写了多少行。至于哪些更新算数,是数据库的说法,不是这个插件的规定——更新匹配到一行但值没变,PostgreSQL 和 MongoDB 算一行,MySQL 算零行。所以只在语句确实改动了值时拿这个数字做比较,上面的例子就是这么写的。
有些后端在批量操作里无法逐行报数。这种情况报的是没有数字,而不是一个错的数字。
变量在语句开始时就被清空,早于任何可能拒绝它的检查;只有在语句跑完、并且能给出精确行数之后才会被写入:
| 变量的内容 | 含义 |
|---|---|
| 一个数字 | 语句执行了,影响了这么多行 |
| 什么都没有 | 没有任何语句给出答复:语句被拒绝或被跳过、失败了,或者后端数不出来 |
零是一个正经答案,不是缺失:语句执行了,只是没匹配到任何行。这正是它有用的地方,而区分这两种情况靠 is set:
if {_rows} is not set:
send "没有任何语句报告写入了多少行。" to console
else if {_rows} is 0:
send "没有匹配到行。" to console
先清空变量,是为了不让上一个语句留下的数字被当成这一条语句的答案。下一行执行时这个数字就已经在了:写入会等自己的活儿干完,所以想让这个子句能读,不需要额外写什么。见 错误与等待。
在事务里它的行为一样,而变量在事务里仍然是逐条语句清空的:被事务跳过的语句没有给出答复,留着上一条语句的数字就变成了假话。事务里特意保留下来的是 last database error,为的是回滚的原因还能被读到。
先读一个值、再按它写入,这是两条语句,其间可能有别的写入插进来。没有事务时,办法是让写入本身去核对它所依据的那个值:
select one entity from table "accounts" and store the result in {_account::*}:
where all:
id = {_from}
set {_balance} to {_account::balance}
loop 3 times:
update entities in table "accounts" with limit 1 and store affected rows in {_rows}:
values:
balance: {_balance} - {_amount}
where all:
id = {_from}
balance = {_balance}
if {_rows} is 1:
stop
# 没人匹配刚才读到的余额,说明这一行动了:重新读一次,再试。
select one entity from table "accounts" and store the result in {_account::*}:
where all:
id = {_from}
set {_balance} to {_account::balance}
where 里重复了脚本读到的那个值,所以只有当这一行仍然是那个值时,更新才可能匹配。影响一行,说明正是这条语句改动了它;零行,说明别人先动手了。
有两点值得记住:
- 要让 MySQL 把它算进去,写入必须真的改动这一行,所以转账金额为
0看起来就像抢输了。与其重试,不如在循环之前就拒绝零金额。 - 重试的圈数要有上限。面对一个不断被写入的行,不肯放弃的循环可以一直转下去;而当需要一起变动的不是一行,而是好几行时,答案是一笔事务。
skript-orm
参考
实用指南
skript-orm (English)
Reference
- Connections
- Tables
- Writing rows
- Reading rows
- Updating and deleting
- Affected rows
- Errors and waiting
- Transactions
- Types
Practical guides
Wiki 由仓库中的 README 和 docs/ 自动生成。修改文档请到仓库提交,直接编辑 Wiki 的内容会在下次同步时被覆盖。
This wiki is generated from the README files and docs/ in the repository. Please submit changes there; direct wiki edits are overwritten on the next sync.