转载请注明出处:https://blog.csdn.net/turtlejj/article/details/105015349,谢谢~
最近在工作中,看到有一部分同事,在查看Android应用程序的数据库文件时,都要先通过adb pull命令,把db文件从手机拷贝到电脑端,再通过一些软件打开查看。这种方式虽然没有什么问题,但在执行某些操作后,数据库发生变化,如果想再次查看,又要再执行一次adb pull操作,很麻烦。
其实,我们完全可以通过adb shell,使用sqlite3这个命令行工具,来查看我们db文件中的表、表结构以及表中存储的数据。
本篇文章,既是我给自己写的一份备忘,也希望能为不熟悉sqlite3工具的同学提供一份命令速查手册,方便大家在使用的时候,不用再去各个地方查找资料。
当然,由于篇幅原因,我们这里并不会列出sqlite3的所有命令,但对于Android应用的开发和调试来说,完全可以满足。
一、Android应用程序的database目录
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打开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模式
退出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的几种显示模式
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 <table> 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;
-
<TR><TD>1</TD>
-
<TD>0</TD>
-
<TD>这是第二条新闻的内容</TD>
-
<TD>1584793640734</TD>
-
<TD>第二条新闻</TD>
-
</TR>
-
-
# 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使用方法的同学带来一些帮助。时间仓促,文中难免会有疏漏,如有错误,欢迎大家予以指正。