转载请注明出处:https://blog.csdn.net/turtlejj/article/details/105015349,谢谢~
最近在工作中,看到有一部分同事,在查看Android应用程序的数据库文件时,都要先通过adb pull命令,把db文件从手机拷贝到电脑端,再通过一些软件打开查看。这种方式虽然没有什么问题,但在执行某些操作后,数据库发生变化,如果想再次查看,又要再执行一次adb pull操作,很麻烦。
其实,我们完全可以通过adb shell,使用sqlite3这个命令行工具,来查看我们db文件中的表、表结构以及表中存储的数据。
本篇文章,既是我给自己写的一份备忘,也希望能为不熟悉sqlite3工具的同学提供一份命令速查手册,方便大家在使用的时候,不用再去各个地方查找资料。
当然,由于篇幅原因,我们这里并不会列出sqlite3的所有命令,但对于Android应用的开发和调试来说,完全可以满足。
Android应用程序,如果需要使用数据库来对数据进行持久化,一定会在应用的目录下生成一个database文件夹,用以存放db文件,如下所示。
其中,.db文件,为应用程序进行数据持久化的数据库文件;而.db-journal文件,则是系统自动生成的日志文件,用于数据库在进行事务时的回滚操作,一般情况下,该文件的大小为0。
pixel:/data/data/com.example.demo/databases # ls -al
total 60
drwxrwx--x 2 u0_a215 u0_a215 4096 2020-03-14 10:25 .
drwx------ 6 u0_a215 u0_a215 4096 2020-03-14 10:25 ..
-rw-rw---- 1 u0_a215 u0_a215 49152 2020-03-14 10:40 demo.db
-rw-rw---- 1 u0_a215 u0_a215 0 2020-03-14 10:40 demo.db-journal
使用sqlite3打开db文件时,可以指定打开的方式为“可写模式”或“只读模式”。(某些旧版本的sqlite3可能没有readonly选项)
# 可写模式打开db文件
pixel:/data/data/com.example.demo/databases # sqlite3 demo.db
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite>
# 只读模式打开db文件
pixel:/data/data/com.example.demo/databases # sqlite3 -readonly demo.db
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite>
退出sqlite3有三种方式,我们这里比较推荐前两种
# 使用".exit"
sqlite>.exit
pixel:/data/data/com.example.demo/databases #
# 使用".quit"命令
sqlite>.quit
pixel:/data/data/com.example.demo/databases #
// 连续按三次"Ctrl + C"
sqlite> ^C^C^C
pixel:/data/data/com.example.demo/databases #
sqlite3在显示数据时,有10种模式,如下所示。
sqlite> .mode mode_name
mode_name list:
ascii Columns/rows delimited by 0x1F and 0x1E # 以ASCII码显示,可能会是乱码
csv Comma-separated values # 以"逗号"分隔
column Left-aligned columns. (See .width) # 以左对齐方式显示
html HTML code # 以html格式显示
insert SQL insert statements for TABLE # 以SQL语句形式展示数据是如何插入的
line One value per line # 单行显示,并显示每个值的变量名
list Values delimited by "|" # 以"|"分隔,这是sqlite3的默认模式
quote Escape answers as for SQL # 类似"insert"模式,但该模式只包含数据插入的内容,但不包含SQL语法
tabs Tab-separated values # 以"tab"分隔
tcl TCL list elements # 将每个值以"双引号"引用,并用空格分隔
我相信,大部分同学在看了上面的解释以后,都不可能完全体会到这10种模式到底会显示成什么样子。我们下面通过实际的例子来给大家展示一下。
# ascii模式
sqlite> .mode ascii
sqlite> select * from news;
10这是第二条新闻的内容1584793640734第二条新闻
# csv模式
sqlite> .mode csv
sqlite> select * from news;
1,0,"这是第二条新闻的内容",1584793640734,"第二条新闻"
# column模式
sqlite> .mode column
sqlite> select * from news;
1 0 这是第二条新闻的内容 1584793640734 第二条新闻
# html模式
sqlite> .mode html
sqlite> select * from news;
1
0
这是第二条新闻的内容
1584793640734
第二条新闻
# insert模式
sqlite> .mode insert
sqlite> select * from news;
INSERT INTO "table" VALUES(1,0,'这是第二条新闻的内容',1584793640734,'第二条新闻');
# line模式
sqlite> .mode line
sqlite> select * from news;
id = 1
commentcount = 0
content = 这是第二条新闻的内容
publishdate = 1584793640734
title = 第二条新闻
# list模式
sqlite> .mode list
sqlite> select * from news;
1|0|这是第二条新闻的内容|1584793640734|第二条新闻
# quote模式
sqlite> .mode quote
sqlite> select * from news;
1,0,'这是第二条新闻的内容',1584793640734,'第二条新闻'
# tabs模式
sqlite> .mode tabs
sqlite> select * from news;
1 0 这是第二条新闻的内容 1584793640734 第二条新闻
# tcl模式
sqlite> .mode tcl
sqlite> select * from news;
"1" "0" "\350\277\231\346\230\257\347\254\254\344\272\214\346\235\241\346\226\260\351\227\273\347\232\204\345\206\205\345\256\271" "1584793640734" "\347\254\254\344\272\214\346\235\241\346\226\260\351\227\273"
通过上面的演示,我们可以很清楚的看到,同样的数据,分别以这10种模式展示出来的样子。其中我们比较常用的模式是list和line。其中list模式,是sqlite3所使用的默认模式,使用"|"对数据进行分隔,每行即为表中的一组数据,十分简洁;但其弊端是,无法得知,被"|"分隔的每个数据,代表表中的哪一列,不够清晰。而line模式,每行只展示一个数据,且会指出该数据在表中的列名,十分清晰;但同样带来的弊端是,如果表的列很多,则打印表中的一行数据,需要占用大量的篇幅,不够简洁。
五、使用“.table”命令查看db文件中的所有表
sqlite> .table
android_metadata news
comment
可以看到,db中共有三张表,其中“news”和“comment”两个表是我们在代码中创建出来的表。而剩下的“android_metadata”,是系统自动创建的。有关这张表的内容,我们会在后面讲到。
六、对表做CURD操作
对表做CURD操作与我们在数据库软件中使用SQL语句完全一样,这里我们只举几个简单的例子,不熟悉SQL语句的同学,可以自行百度,该部分内容不在我们本篇文章的讲解范围之内。
注意,如果打开db文件时,使用了-readonly参数,则只能对表进行查询操作,不能执行insert、delete和update操作。
# 查询"news"表,得到一条数据
sqlite> select * from news;
id = 1
commentcount = 2
content = 这是第一条新闻的内容
publishdate = 1584793640734
title = 第一条新闻
# 查询"news"表中的数据个数
sqlite> select count(*) from news;
count(*) = 1
# 向"news"表中插入一条数据
sqlite> insert into "news" values(2,0,'这是第二条新闻的内容',1584793642000,'第二条新闻');
# 查询"news"表,得到两条数据
sqlite> select * from news;
id = 1
commentcount = 2
content = 这是第一条新闻的内容
publishdate = 1584793640734
title = 第一条新闻
id = 2
commentcount = 0
content = 这是第二条新闻的内容
publishdate = 1584793642000
title = 第二条新闻
# 从"news"中删除"id"为1的数据
sqlite> delete from 'news' where id=1;
# 查询"news"表,只剩下"id"为2的数据
sqlite> select * from news;
id = 2
commentcount = 0
content = 这是第三条新闻的内容
publishdate = 1584793642000
title = 第二条新闻
# 更新"news"中"id"为2的数据,将commentcount列的值改为99
sqlite> update 'news' set commentcount=99 where id=2;
# 查询"news"表,"id"为2的数据的commentcount列的值变为99
sqlite> select * from news;
id = 2
commentcount = 99
content = 这是第二条新闻的内容
publishdate = 1584793642000
title = 第二条新闻
至此,使用sqlite3对db进行基本操作的讲解,就告一段落了。但本文还没有结束,如果你觉得以上的操作已经完全能够满足你的需求,那就可以到此为止了。当然,如果你希望了解更多,欢迎继续往下阅读。
七、查看表的结构及建表语句
1. 使用“pragma table_info(table_name)”命令查看表结构
通过“pragma table_info(table_name) ”命令,我们可以很清晰的看到表的接口,和其中每列的各种属性。
sqlite> pragma table_info(news);
cid = 0 # 列ID
name = id # 列名
type = integer # 数据类型
notnull = 0 # 是否可以为空
dflt_value = # 默认值
pk = 1 # 是否为主键
cid = 1
name = commentcount
type = integer
notnull = 0
dflt_value =
pk = 0
cid = 2
name = content
type = text
notnull = 0
dflt_value =
pk = 0
cid = 3
name = publishdate
type = integer
notnull = 0
dflt_value =
pk = 0
cid = 4
name = title
type = text
notnull = 0
dflt_value =
pk = 0
但有时候,想将表的结构,分享给其他人时,如果拷贝这么一大堆内容,其他人在看起来的时候,也会很吃力,有没有什么办法,可以得到建表时所使用的语句呢?当然可以,我们继续往下看。
2. 查看建表语句
想要查询db的建表语句,有两种方式:
方法一 使用“.schema”命令查看建表语句
sqlite> .schema
# "android_metadata"表的建表语句
CREATE TABLE android_metadata (locale TEXT);
# "news"表的建表语句
CREATE TABLE news (id integer primary key autoincrement,commentcount integer, content text, publishdate integer, title text);
# "comment"表的建表语句
CREATE TABLE comment (id integer primary key autoincrement,content text, publishdata integer, news_id integer);
如果只想查看某一张表的建表语句,我们可以指定表名,以得到更简洁的结果
# 执行表名以得到某一张表的建表语句
sqlite> .schema news
CREATE TABLE news (id integer primary key autoincrement,commentcount integer, content text, publishdate integer, title text);
# 使用"--indent"参数,可以得到pretty-printing
sqlite> .schema --indent comment
CREATE TABLE comment(
id integer primary key autoincrement,
content text,
publishdata integer,
news_id integer
);
# 注意,不要加分号,否则不会打印出任何信息
sqlite> .schema news;
sqlite>
方法二 SQLite创建数据库时,会建立一张特殊的表“sqlite_master”,称为系统表(也有说法叫内置表),用以保存每张表的建表语句。我们可以通过查询该表的内容,来获得建表语句。
sqlite> select * from sqlite_master;
type = table
name = android_metadata
tbl_name = android_metadata
rootpage = 3
sql = CREATE TABLE android_metadata (locale TEXT)
type = table
name = sqlite_sequence
tbl_name = sqlite_sequence
rootpage = 4
sql = CREATE TABLE sqlite_sequence(name,seq)
type = table
name = news
tbl_name = news
rootpage = 5
sql = CREATE TABLE news (id integer primary key autoincrement,commentcount integer, content text, publishdate integer, title text)
type = index
name = sqlite_autoindex_news_1
tbl_name = news
rootpage = 6
type = table
name = comment
tbl_name = comment
rootpage = 7
sql = CREATE TABLE comment (id integer primary key autoincrement,content text, publishdata integer, news_id integer)
其中,的字段含义如下所示:
type # 记录了项目的类型,如table、index、view、trigger。
name # 记录了项目的名称,如表名、索引名等。
tbl_name # 记录所从属的表名,如索引所在的表名。对于表来说,该列就是表名本身。
rootpage # 记录项目在数据库页中存储的编号。对于视图和触发器,该列值为0或者NULL。
sql # 记录创建该项目的SQL语句。
当然,我们也可以只查询某一张表的信息,可以得到更简洁的信息
sqlite> select * from sqlite_master where name='news';
type = table
name = news
tbl_name = news
rootpage = 6
sql = CREATE TABLE news (id integer primary key autoincrement,commentcount integer, content text, publishdate integer, title text)
相比于方法一,方法二中查询到的信息更全面。同时,也引出了几个系统默认创建的表,它们分别是“android_metadata”,“sqlite_sequence”和“sqlite_master”。
其中“sqlite_master”表,我们在本节已经介绍过了,可以通过其查看每张表的建表语句,后面不再赘述。
“android_metadata”在前面我们使用“.table”命令列出数据库中的表时,已经见到过了,但尚未对其进行分析讲解;而“sqlite_sequence”,我们至今还没有提到过,下面,我们就来对后两张表进行逐一分析。
五. 系统创建的表
1. “android_metadata”
首先,我们来看一下这张表的建表语句和表中存储的内容。
# 建表语句
sqlite> .schema android_metadata
CREATE TABLE android_metadata (locale TEXT);
# 存储内容
sqlite> select * from android_metadata;
locale = zh_CN
其实,这样一看,“android_metadata”表的作用就很清晰了,它是用来存储“默认语言环境”的,其中“zh_CN”就表示“简体中文,中华人民共和国”。当然,如果手机的默认语言环境如果是美式英语的话,那“locale”的值将会是“en-US”。
关于“android_metadata”表和“Locale”的内容,如果大家有兴趣,可以自行阅读源码中的“SQLiteConnection.java”和“Locale.java”文件。
2. “sqlite_sequence”
老样子,还是先来看建表语句和表中存储的内容。
# 建表语句
sqlite> .schema sqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
# 存储内容
sqlite> select * from sqlite_sequence;
name = comment
seq = 2
name = news
seq = 2
“name”字段,没什么可以说的,就是表名。
而“seq”是做什么用的呢?我们先来分别查询一下这两张表中的数据。
# 查询"news"表中的数据,只有一条记录
sqlite> select * from news;
id = 2
commentcount = 99
content = 这是第三条新闻的内容
publishdate = 1584793642000
title = 第三条新闻
# 查询"comment"表中的数据,有两条记录
sqlite> select * from comment;
id = 1
content = 好评!
publishdata = 1584793640700
news_id = 2
id = 2
content = 赞一个!
publishdata = 1584793640719
news_id = 2
“comment”表中,有两条记录,与“sqlite_sequence”表中关于“comment”的“seq”的值吻合,但“news”表中只有一条,为什么“sqlite_sequence”表中关于“news”的“seq”值也是2呢?
注意观察,我们发现,无论“news”表,还是“comment”表,其最大的id值,均为2。那是不是说,“sqlite_sequence”表中记录的“seq”值,有可能是每张表中id的最大值呢?我们不妨来做个实验。
# 像"new"表中插入一条id为8的数据
sqlite> INSERT INTO "news" VALUES(8,1002,'This is Only for Test',123123123,'Test');
# 查询"news"表中的数据
sqlite> select * from news;
id = 2
commentcount = 99
content = 这是第三条新闻的内容
publishdate = 1584793642000
title = 第三条新闻
id = 8
commentcount = 1002
content = This is Only for Test
publishdate = 123123123
title = Test
# 再次查询"sqlite_sequence"表中的数据
sqlite> select * from sqlite_sequence;
name = comment
seq = 2
name = news
seq = 8
诶,“sqlite_sequence”中关于“news”表的“seq”值果然变为8了,说明我们之前的猜想是正确的。那么,为什么要记录这个值呢?我们不妨再来看看“news”表和“comment”表的建表语句。
# "news"表的建表语句
CREATE TABLE news (id integer primary key autoincrement,commentcount integer, content text, publishdate integer, title text);
# "comment"表的建表语句
CREATE TABLE comment (id integer primary key autoincrement,content text, publishdata integer, news_
发现没有,两张表的id值,均为autoincrement类型。因此,数据库需要在每次插入数据时,记录每张表当前的id值,以便在下次向表中插入数据时,对该值进行+1操作。
至此,所有要分析的内容都已经结束了,希望本文能对那些还不太熟悉sqlite3使用方法的同学带来一些帮助。时间仓促,文中难免会有疏漏,如有错误,欢迎大家予以指正。
你可能感兴趣的:(工具使用)