上篇文章简单讨论了虚拟机的原理,这篇文章我们详细讨论下指令,具体从几种典型的SQL语句来看看每种SQL对应的指令流,以及每个指令的含义。通过explain语句,可以看到语句对应的指令流;通过pragma vdbe_trace=on指令,我们甚至可以得到语句对应的指令执行流程,包括跳转等。
测试表结构

CREATE TABLE t1(id integer primary key autoincrement, user_id int, c1 varchar(1000), c2 varchar(1000)
);

测试语句
(1)INSERT

sqlite> explain insert into t1(user_id,c1,c2) values(1111,'abc','abc');
0|Init|0|17|0||00|Start at 17
1|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
2|NewRowid|0|4|2||00|r[4]=rowid
3|MemMax|2|4|0||00|r[2]=max(r[2],r[4])
4|SoftNull|5|0|0||00|r[5]=NULL
5|Integer|1111|6|0||00|r[6]=1111
6|String8|0|7|0|abc|00|r[7]='abc'
7|String8|0|8|0|abc|00|r[8]='abc'
8|MakeRecord|5|4|9|DDBB|00|r[9]=mkrec(r[5..8])
9|Insert|0|9|4|t1|1b|intkey=r[4] data=r[9]
10|Close|0|0|0||00|
11|OpenWrite|0|3|0|2|00|root=3 iDb=0; sqlite_sequence
12|NotNull|3|14|0||00|if r[3]!=NULL goto 14
13|NewRowid|0|3|0||00|r[3]=rowid
14|MakeRecord|1|2|10||00|r[10]=mkrec(r[1..2])
15|Insert|0|10|3||08|intkey=r[3] data=r[10]
16|Halt|0|0|0||00|
17|Transaction|0|1|4|0|01|
18|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
19|TableLock|0|3|1|sqlite_sequence|00|iDb=0 root=3 write=1
20|OpenRead|0|3|0|2|00|root=3 iDb=0; sqlite_sequence
21|Null|0|2|3||00|r[2..3]=NULL
22|String8|0|1|0|t1|00|r[1]='t1'
23|Rewind|0|31|0||00|
24|Column|0|0|2||00|r[2]=
25|Ne|1|29|2||10|if r[1]!=r[2] goto 29
26|Rowid|0|3|0||00|r[3]=rowid
27|Column|0|1|2||00|r[2]=
28|Goto|0|31|0||00|
29|Next|0|24|0||00|
30|Integer|0|2|0||00|r[2]=0
31|Close|0|0|0||00|
32|Goto|0|1|0||00|

通过explain我们得到了语句对应的指令流,该语句总共包含了32条指令,下面我们逐条来说明指令的含义。

 

指令

含义

0

Init|0|17|0||00|Start at 17

指令从P2(17)开始

1

OpenWrite|0|5|0|4|00|root=5 iDb=0; t1

打开表t1读写游标cursor0,P4,表示总共有4列

2

NewRowid|0|4|2||00|r[4]=rowid

生成一个newRowid,写入P2寄存器,P3寄存器存储的是目前最大值,当rowid达到最大值,则SQLITE_FULL error。

3

MemMax|2|4|0||00|r[2]=max(r[2],r[4])

取出sqlite_sequence里面和表里面记录的最大值

4

SoftNull|5|0|0||00|r[5]=NULL

初始化r[5]为null,rowid列

5

Integer|1111|6|0||00|r[6]=1111

设置寄存器r[6]为1111

6

String8|0|7|0|abc|00|r[7]='abc'

设置寄存器r[7]为abc

7

String8|0|8|0|abc|00|r[8]='abc'

设置寄存器r[8]为abc

8

MakeRecord|5|4|9|DDBB|00|r[9]=mkrec(r[5..8])

生成t1表记录

将寄存器r[5..8]的内容转为记录格式,存入r[9],P2指定长度为4。

9

Insert|0|9|4|t1|1b|intkey=r[4] data=r[9]

插入

key在r[4]中,data在r[9]寄存器中

10

Close|0|0|0||00|

关闭游标

11

OpenWrite|0|3|0|2|00|root=3 iDb=0; sqlite_sequence

打开写游标

12

NotNull|3|14|0||00|if r[3]!=NULL goto 14

r[3]不为null,则跳转14

13

NewRowid|0|3|0||00|r[3]=rowid

生成新的rowid

14

MakeRecord|1|2|10||00|r[10]=mkrec(r[1..2])

生成sqlite_sequence记录

生成记录,r[1]=t1,r[2]=seq

15

Insert|0|10|3||08|intkey=r[3] data=r[10]

插入

key=r[3],data=r[10]

16

Halt|0|0|0||00|

终止

17

Transaction|0|1|4|0|01|

打开一个新事务,创建回滚日志文件

18

TableLock|0|5|1|t1|00|iDb=0 root=5 write=1

上t1表锁(仅用于shared-cache)

19

TableLock|0|3|1|sqlite_sequence|00|iDb=0 root=3 write=1

上sqlite_sequence表锁

20

OpenRead|0|3|0|2|00|root=3 iDb=0; sqlite_sequence

打开sqlite_sequence表,P4,表示总共2列

21

Null|0|2|3||00|r[2..3]=NULL

初始化寄存器为NULL

22

String8|0|1|0|t1|00|r[1]='t1'

设置r[1]为t1

23

Rewind|0|31|0||00|

重置游标cursor P1,游标指向索引的第一个位置,如果tree为空,则跳转到31,否则执行下面的指令

24

Column|0|0|2||00|r[2]=

获取cusor P1指向记录的P2th列,即sqlite_sequence的name列,结果存在r[2]中

25

Ne|1|29|2||10|if r[1]!=r[2] goto 29

如果sqlite_sequence表中记录与当前的表名不一致,跳转到29

26

Rowid|0|3|0||00|r[3]=rowid

获取当前记录的rowid

27

Column|0|1|2||00|r[2]=

获取当前记录的第1列,结果存放在r[2]中,sqlite_sequence的第一列是seq值

28

Goto|0|31|0||00|

跳转到31

29

Next|0|24|0||00|

游标往后移,如果还有记录,则跳转到P2(24),否则继续往后执行。

30

Integer|0|2|0||00|r[2]=0

这是r[2]为0

31

Close|0|0|0||00|

关闭sqlite_sequence表的游标

32

Goto|0|1|0||00|

跳转到1,开始执行插入表t1

通过pragma命令,设置vdbe_trace为on可以看到SQL语句对应的指令流是如何运行的,具体如下:可以看到,指令并不是顺序执行的,而是存在跳转,具体的执行顺序,由代码生成器生成指令流和指令的内容决定。

sqlite> pragma vdbe_trace=on;
sqlite> insert into t1(user_id,c1,c2) values(1111,'abc','abc');
SQL: [insert into t1(user_id,c1,c2) values(1111,'abc','abc');]
VDBE Trace:
0 Init 0 17 0 00 Start at 17
17 Transaction 0 1 4 0 01
18 TableLock 0 5 1 t1 00 iDb=0 root=5 write=1
19 TableLock 0 3 1 sqlite_sequence 00 iDb=0 root=3 write=1
20 OpenRead 0 3 0 2 00 root=3 iDb=0; sqlite_sequence
21 Null 0 2 3 00 r[2..3]=NULL
REG[2] = NULL
22 String8 0 1 0 t1 00 r[1]='t1'
REG[1] = t2[t1](8)
23 Rewind 0 31 0 00
24 Column 0 0 2 00 r[2]=
REG[2] = s4[user](8)
25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
REG[1] = t2[t1](8)
REG[2] = s4[user](8)
29 Next 0 24 0 00
24 Column 0 0 2 00 r[2]=
REG[2] = s6[orders](8)
25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
REG[1] = t2[t1](8)
REG[2] = s6[orders](8)
29 Next 0 24 0 00
24 Column 0 0 2 00 r[2]=
REG[2] = s2[t1](8)
25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
REG[1] = t2[t1](8)
REG[2] = s2[t1](8)
26 Rowid 0 3 0 00 r[3]=rowid
REG[3] = i:3
27 Column 0 1 2 00 r[2]=
REG[2] = i:113
28 Goto 0 31 0 00
31 Close 0 0 0 00
32 Goto 0 1 0 00
1 OpenWrite 0 5 0 4 00 root=5 iDb=0; t1
2 NewRowid 0 4 2 00 r[4]=rowid
REG[2] = i:113
REG[4] = i:114
3 MemMax 2 4 0 00 r[2]=max(r[2],r[4])
REG[4] = i:114
4 SoftNull 5 0 0 00 r[5]=NULL
5 Integer 1111 6 0 00 r[6]=1111
REG[6] = i:1111
6 String8 0 7 0 abc 00 r[7]='abc'
REG[7] = t3[abc](8)
7 String8 0 8 0 abc 00 r[8]='abc'
REG[8] = t3[abc](8)
8 MakeRecord 5 4 9 DDBB 00 r[9]=mkrec(r[5..8])
REG[9] = s13[05000213130457616263616263......Wabcabc](8)
9 Insert 0 9 4 t1 1B intkey=r[4] data=r[9]
REG[9] = s13[05000213130457616263616263......Wabcabc](8)
REG[4] = i:114
10 Close 0 0 0 00
11 OpenWrite 0 3 0 2 00 root=3 iDb=0; sqlite_sequence
12 NotNull 3 14 0 00 if r[3]!=NULL goto 14
REG[3] = i:3
14 MakeRecord 1 2 10 00 r[10]=mkrec(r[1..2])
REG[10] = s6[031101743172...t1r](8)
15 Insert 0 10 3 08 intkey=r[3] data=r[10]
REG[10] = s6[031101743172...t1r](8)
REG[3] = i:3
16 Halt 0 0 0 00

(2)SELECT
sqlite> explain select rowid,user_id,c2 from t1 where rowid=112;
0|Init|0|12|0||00|Start at 12
1|OpenRead|0|5|0|4|00|root=5 iDb=0; t1
2|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
3|Integer|112|1|0||00|r[1]=112
4|MustBeInt|1|10|0||00|
r[p1]中的值必需为int,或可以转为int,如果不能跳转到10。
5|NotExists|0|10|1||00|intkey=r[1]; pk
P3是rowid,查找表,判断是否有rowid记录,如果没有,则跳转10
6|Copy|1|2|0||00|r[2]=r[1]
7|Column|0|1|3||00|r[3]=t1.user_id
读取第1列到寄存器r[3]
8|Column|0|3|4||00|r[4]=t1.c2
读取第3列到寄存器r[4]
9|ResultRow|2|3|0||00|output=r[2..4]
生成结果集
10|Close|0|0|0||00|
11|Halt|0|0|0||00|
关闭所有打开的游标,P1是返回给用户的错误码,根据P2值确定是否需要rollback。
12|Transaction|0|0|7|0|01|
13|TableLock|0|5|0|t1|00|iDb=0 root=5 write=0
14|Goto|0|1|0||00|

(3)UPDATE
sqlite> explain update t1 set user_id=888 where rowid=111;
addr|opcode|p1|p2|p3|p4|p5|comment
0|Init|0|16|0||00|Start at 16
1|Null|0|1|2||00|r[1..2]=NULL
2|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
打开写游标
3|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
4|Integer|111|7|0||00|r[7]=111
5|MustBeInt|7|8|0||00|
6|NotExists|0|8|7||00|intkey=r[7]; pk
查不到则跳转8
7|Rowid|0|2|0||00|r[2]=rowid
8|IsNull|2|15|0||00|if r[2]==NULL goto 15
没有找到,跳转15结束
9|Null|0|3|0||00|r[3]=NULL
10|Integer|888|4|0||00|r[4]=888
11|Column|0|2|5||00|r[5]=t1.c1
12|Column|0|3|6||00|r[6]=t1.c2
13|MakeRecord|3|4|8|DDBB|00|r[8]=mkrec(r[3..6])
新建record
14|Insert|0|8|2|t1|05|intkey=r[2] data=r[8]
同一个rowid,进行覆盖。
15|Halt|0|0|0||00|
16|Transaction|0|1|7|0|01|
开启写事务
17|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
对表t1上写锁
18|Goto|0|1|0||00|

(4)DELETE
sqlite> explain delete from t1 where rowid=111;
addr|opcode|p1|p2|p3|p4|p5|comment
0|Init|0|11|0||00|Start at 11
1|Null|0|1|0||00|r[1]=NULL
2|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
3|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
4|Integer|111|2|0||00|r[2]=111
5|MustBeInt|2|8|0||00|
6|NotExists|0|8|2||00|intkey=r[2]; pk
7|Goto|0|9|0||00|
8|Goto|0|10|0||00|
9|Delete|0|1|0|t1|00|
删除游标所指的记录
10|Halt|0|0|0||00|
11|Transaction|0|1|7|0|01|
12|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
13|Goto|0|1|0||00|

小结
      通过上面的INSERT,SELECT,UPDATE,DELETE语句,我简单介绍了语句中包含的指令,以及指令的含义。经过这个过程,相信大家对SQLite执行流程有了更深的认识,也更能理解指令是如何存取数据的。SQLite中总共包含了100多条指令,对于每条指令的详细含义可以参考官方文档:https://www.sqlite.org/opcode.html

转载于:https://www.cnblogs.com/cchust/p/5143666.html

SQLite学习笔记(十二)虚拟机指令相关推荐

  1. 汇编语言学习笔记(十二)-浮点指令

    浮点数如何存储 浮点寄存器 浮点数指令 浮点计算例子 浮点高级运算 CMOV移动指令 浮点数如何存储 浮点数的运算完全不同于整数,从寄存器到指令,都有一套独特的处理流程,浮点单元也称作x87 FPU. ...

  2. 吴恩达《机器学习》学习笔记十二——机器学习系统

    吴恩达<机器学习>学习笔记十二--机器学习系统 一.设计机器学习系统的思想 1.快速实现+绘制学习曲线--寻找重点优化的方向 2.误差分析 3.数值估计 二.偏斜类问题(类别不均衡) 三. ...

  3. ROS学习笔记十二:使用roswtf

    ROS学习笔记十二:使用roswtf 在使用ROS过程中,roswtf工具可以为我们提供ROS系统是否正常工作的检查作用. 注意:在进行下列操作之前,请确保roscore没有运行. 检查ROS是否安装 ...

  4. Python语言入门这一篇就够了-学习笔记(十二万字)

    Python语言入门这一篇就够了-学习笔记(十二万字) 友情提示:先关注收藏,再查看,12万字保姆级 Python语言从入门到精通教程. 文章目录 Python语言入门这一篇就够了-学习笔记(十二万字 ...

  5. Polyworks脚本开发学习笔记(十二)-输出和读取文本文件

    Polyworks脚本开发学习笔记(十二)-输出和读取文本文件 Polyworks作为一个测量工具,将测量的数据方便的导出到文本文件则是一项必须的功能.在DATA_FILE这个命令下提供了很多子命令用 ...

  6. OpenCV学习笔记(十二)——图像分割与提取

    在图像处理的过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来.例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆.行人或者其他对象. ...

  7. 【现代机器人学】学习笔记十二:轮式移动机器人

    目录 轮式机器人类型 全向轮式机器人 建模 单个全向轮是怎么运动的 多个全向轮是如何带动底盘运动的 运动规划和反馈控制 非完整约束轮式移动机器人 建模 独轮车 差速驱动机器人 车型机器人 非完整移动机 ...

  8. Vue.js 学习笔记 十二 Vue发起Ajax请求

    首先需要导入vue-resource.js,可以自己下载引入,也可以通过Nuget下载,它依赖于Vue.js. 全局使用方式: Vue.http.get(url,[options]).then(suc ...

  9. 【theano-windows】学习笔记十二——卷积神经网络

    前言 按照进度, 学习theano中的卷积操作 国际惯例, 来一波参考网址 Convolutional Neural Networks (LeNet) 卷积神经网络如何应用在彩色图像上? 卷积小知识 ...

最新文章

  1. ant引入html页面,antd引入普通html使用,将ant Design本地化
  2. JSON中的安全问题
  3. mysql创建表对经常要查询的列添加索引或者组合索引
  4. 团体程序设计天梯赛-练习集L1-013. 计算阶乘和
  5. 了解CUDA计算(一)
  6. efi分区咋移动到c盘里_怎么手动安装CLOVER到U盘EFI分区
  7. php有哪些高级扩展,php扩展有哪些
  8. 计算机设计大赛国奖作品_1. 项目概要
  9. 「雕爷学编程」Arduino动手做(26)——4X4矩阵键盘模块
  10. java中线程池的使用方法
  11. C#.Net工作笔记016---C#中使用反射对两个对象copy复制内容
  12. 机器学习方法(二)——xgboost(原理篇)
  13. topcoder java_Topcoder 练习小记,Java 与 Python 分别实现。
  14. rounding mode
  15. 闲话Variable Selection和Lasso
  16. 如何删除PDF水印,PDF删除水印的方法
  17. 深度学习不得不知的英文名称
  18. 超融合解决方案已成新黑马 市场排名初见端倪
  19. 笔记本光驱在计算机里不显示器,笔记本怎么解决识别不了光驱
  20. linux根文件系统配置,Linux学习笔记__ Linux根文件系统详解

热门文章

  1. 软件设计师备考知识04
  2. 程序员加班制作了30张可视化大屏模板,套用数据直接用
  3. 如何用研发流程搞垮一个团队?
  4. web报表工具FineReport使用中遇到的常见报错及解决办法(二)
  5. python打开快捷方式_Python打开一个JAR快捷方式
  6. python画散点图带直线和图例_带图例的Matplotlib散点图
  7. oracle用分号拼接函数,ORACLE以逗号分隔连接列的值   函数名:wmsys.wm_concat
  8. centos 删除crontab_Centos7:利用crontab定时执行任务
  9. unity直播推流方式_【技术猩球】从方案架构分析秀场直播的四种实现方式
  10. mysql实验视图及索引_MySQL视图及索引