Android SQLite数据库进行查询优化的方法
作者:想飞的鱼 发布时间:2022-10-03 09:09:00
前言
数据库的性能优化行业里面普遍偏少,今天这篇希望给大家带来点帮助
SQLite是个典型的嵌入式DBMS,它有很多优点,它是轻量级的,在编译之后很小,其中一个原因就是在查询优化方面比较简单
我们在使用SQLite进行数据存储查询的时候,要进行查询优化,这里就会用到索引,C端的数据量大部分情况下面虽然不是很大,但良好的索引建立习惯往往会带来不错的查询性能提升,同时在未知的将来经得住更大数据的考验,那如何优化数据库查询呢,下面我们用例子一一演示下。
先建个测试表table1,包含了三个索引:
sqlite> .schem
CREATE TABLE table1(id integer primary key not null default 0,a integer,b integer, c integer);
CREATE INDEX a_i on table1 (a);
CREATE INDEX a_i2 on table1 (a,b);
CREATE INDEX a_i3 on table1 (c);
在常见的数据库系统里面,进行SQL查询检验都是用explain关键字,比如:
sqlite> explain select * from table1;
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 10 0 00 Start at 10
1 OpenRead 0 2 0 4 00 root=2 iDb=0; table1
2 Rewind 0 9 0 00
3 Rowid 0 1 0 00 r[1]=rowid
4 Column 0 1 2 00 r[2]=table1.a
5 Column 0 2 3 00 r[3]=table1.b
6 Column 0 3 4 00 r[4]=table1.c
7 ResultRow 1 4 0 00 output=r[1..4]
8 Next 0 3 0 01
9 Halt 0 0 0 00
10 Transaction 0 0 4 0 01 usesStmtJournal=0
11 Goto 0 1 0 00
立马就会得到输出,这些输出表示SQLite执行这条SQL用到的每句指令,这个其实不怎么直观,我们用到更多的是EXPLAIN QUERY PLAN,如下:
sqlite> explain QUERY PLAN select * from table1;
0|0|0|SCAN TABLE table1
这条SQL语句是查询了整张表,所以结果关键字SCAN表示要完整遍历,这种效率是最低的,接下来我们试试加个查询条件:
sqlite> explain QUERY PLAN select * from table1 where a=1;
0|0|0|SEARCH TABLE table1 USING INDEX a_i2 (a=?)
加上where a=1之后关键字变成了SEARCH,表示不再需要遍历了,而是使用了索引进行了部分检索,另外这条输出还有更多信息,比如使用了索引a_i2,而括号里面的a=?则表示是这个查询条件引起的
我们稍微修改下SQL:
sqlite> explain QUERY PLAN select a from table1 where a=1;
0|0|0|SEARCH TABLE table1 USING COVERING INDEX a_i (a=?)
把select 变成了select a,发现explain输出有细微变化,从INDEX变成了COVERING INDEX,CONVERING INDEX表示直接使用索引查询就可以得到结果,不需要再次回查数据表,这样效率更高。而之前的查询因为是使用,索引里面只有a记录,所以必须要查询原始记录才能得到b,c字段。我们再试下这条SQL:
sqlite> explain QUERY PLAN select a,b from table1 where a=1 and b=1;
0|0|0|SEARCH TABLE table1 USING COVERING INDEX a_i2 (a=? AND b=?)
同意因为索引a_i2已经包含a和b了,所以也是使用CONVERING INDEX。那有同学可能会问了,那我们建索引的时候都把其他字段都加进去呗,虽然查询用不到,但不用二次查询原始记录效率高。理论上这样是可行的,但这里有个重要问题就是数据冗余太严重了,导致索引和原始数据一样大,在海量数据存储的数据库里面磁盘消耗是个问题,所以如何选择可能要做个平衡。
接下来我们把and换成or:
sqlite> explain QUERY PLAN select a,b from table1 where a=1 or b=1;
0|0|0|SCAN TABLE table1 USING COVERING INDEX a_i2
发现又变回SCAN了,但仍然使用到了索引a_i2,对比下这条SQL:
sqlite> explain QUERY PLAN select a,b from table1 where a=1;
0|0|0|SEARCH TABLE table1 USING COVERING INDEX a_i2 (a=?)
多了个查询条件b=1之后效率变差了,这是为什么呢?这里要引出我们创建索引使用的最关键的原则:前缀索引。
索引一般是使用B树,前缀索引简单来讲,就是要想能使用这个索引,查询条件必须满足索引建立涉及到的字段,并且和查询使用的顺序一致。
我们回头看刚才那个or的例子,对于查询条件a=1,他能使用a_i2(a,b)这个索引,因为索引顺序也是a开头的。但or的例子里面还或上一个查询条件b=1,对于这个查询就没有索引可以用了,因为没有b开头的索引存在。a_i2(a,b)这个索引里面虽然有b,但b对于b=1这个查询条件来说不是在前面,不满足前缀索引原则。
而对于刚才那个and的例子,则能够完全使用索引,因为存在索引a_i2(a,b),可以想象成先按索引a过滤数据,剩下数据再用索引b过滤数据。对于and条件来说,索引里面字段的顺序换一下也是没有关系的,数据库会自动优化选择,比如:
sqlite> .schem
CREATE INDEX a_i22 on table2 (b,a);
sqlite> explain QUERY PLAN select a,b from table2 where a=1 and b=1;
0|0|0|SEARCH TABLE table2 USING COVERING INDEX a_i22 (b=? AND a=?)
如果or查询也要充分使用索引,聪明的读者一定想到了,那就是要建2个索引,如下:
CREATE TABLE table3(id integer primary key not null default 0,a integer,b integer, c integer);
CREATE INDEX a_i222 on table3(a);
CREATE INDEX a_i2222 on table3(b);
sqlite> explain QUERY PLAN select a,b from table3 where a=1 or b=1;
0|0|0|SEARCH TABLE table3 USING INDEX a_i222 (a=?)
0|0|0|SEARCH TABLE table3 USING INDEX a_i2222 (b=?)
我们再来看一个进阶的,加上一个排序:
CREATE TABLE table1(id integer primary key not null default 0,a integer,b integer, c integer);
CREATE INDEX a_i2 on table1 (a,b);
sqlite> explain QUERY PLAN select a,b from table1 where a=1 order by b;
0|0|0|SEARCH TABLE table1 USING COVERING INDEX a_i2 (a=?)
CREATE TABLE table3(id integer primary key not null default 0,a integer,b integer, c integer);
CREATE INDEX a_i222 on table3(a);
CREATE INDEX a_i2222 on table3(b);
sqlite> explain QUERY PLAN select a,b from table3 where a=1 order by b;
0|0|0|SEARCH TABLE table3 USING INDEX a_i222 (a=?)
0|0|0|USE TEMP B-TREE FOR ORDER BY
对比这2个查询,发现下面这个多了个USE TEMP B-TREE FOR ORDER BY。对于第一个查询来说,我们可以看到排序也是同样满足前缀索引原则(先按索引a过滤数据,剩下数据用索引b排序)。对于第二个查询来说,因为不满足这个原则导致多了个临时表来做排序。看到这里大家应该理解前缀索引的意思了。
我们再看这个样子,把查询条件和排序换下:
sqlite> explain QUERY PLAN select a,b from table1 where b=1 order by a;
0|0|0|SCAN TABLE table1 USING COVERING INDEX a_i2
显然不满足前缀索引原则了,因为需要先按索引b过滤数据,但b不是第一个。
常规的查询语句大部分是and,or,order的组合使用,只需要掌握上面说的原则,一定能写出高性能的数据库查询语句来。
而对于更高级的一些连表可以继续翻阅官方文档:
https://www.sqlite.org/eqp.html
https://www.sqlite.org/lang_e...
来源:https://segmentfault.com/a/1190000016754594


猜你喜欢
- 1.分支结构的概念当需要进行条件判断并做出选择时,使用分支结构2.if分支结构格式:if(条件表达式){语句块;}package com.l
- 本文实例为大家分享了java实现短信验证码5分钟有效时间,供大家参考,具体内容如下实现一个发送短信验证码的请求,要求5分钟之内重复请求,返回
- 故障:收到服务器报警,内存使用率超过80%1.查看使用dstat和top查看内存使用最高的应用使用dstat查到内存占用最高的是java应用
- 本文实例讲述了Java中计算时间差的方法。分享给大家供大家参考。具体如下:假设现在是2004-03-26 13:31:40过去是:2004-
- 本文实例讲述了C#画笔Pen绘制自定义线的帽子。分享给大家供大家参考。具体如下:using System;using System.Coll
- 本文为大家分享了Unity3D飞机大战游戏第一部分的实现代码,供大家参考,具体内容如下让飞机可以发射 * 准备工作:1、将 * 设置成预制体2、
- 最近一个项目中,需要用到Java的websocket新特性,于是就学了一下,感觉这技术还挺好玩的,瞬间知道网页上面的那些在线客服是怎么做的了
- 本文实例为大家分享了springboot实现异步任务的具体代码,供大家参考,具体内容如下1.什么异步任务同步:一定要等任务执行完了,得到结果
- 这篇文章主要介绍了SpringBoot如何通过devtools实现热部署,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 一、概述在软件开发中,有时需要保存一个对象的状态,以便于允许用户取消相关操作或者从以往的状态中恢复过来。比如一个文档版本管理系统,可以根据需
- 配置Servlet的方法有俩种,分别是传统web.xml文档中部署servlet和注解方式部署servlet,下面就先一起来学习 * 解方式部
- 本文实例讲述了java中struts2实现文件上传下载功能实现方法。分享给大家供大家参考。具体分析如下:1.文件上传首先是jsp页面的代码在
- 一、前提准备安装R软件,需要安装32位的R软件,64位的调用会报错。另外就是讲R添加到电脑环境变量中。打开R软件,安装包 scatterpl
- 前言人类建造迷宫已有5000年的历史。在世界的不同文化发展时期,这些奇特的建筑物始终吸引人们沿着弯弯曲曲、困难重重的小路吃力地行走,寻找真相
- 导出excel是咱Java开发的必备技能啦,之前项目有这个功能,现在将其独立出来,分享一下。所用技术就是SpringBoot,然后是MVC架
- 本文总结三种用于安卓录屏的解决方案:adb shell命令screenrecordMediaRecorder, MediaProjectio
- 一、总体说明 XML和JSON 是最为常用的数据交换格式本例子演示如何将java对象,转成XML输出。二、流程1.在上文的例子中,创建一个包
- AuditEnum.cs:public enum AuditEnum{ Holding=0, Audit
- java超市管理系统 1.0(含源文件,后续会继续优化~)前言一个月零零散散的时间学习了java,通过这次“超市管理系统”的练习,希望可以给
- 本文实例讲述了Android编程实现google消息通知功能。分享给大家供大家参考,具体如下:1. 定义一个派生于WakefulBroadc