sqlite知识分享

Posted Wastrel_xyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sqlite知识分享相关的知识,希望对你有一定的参考价值。

数据结构模型

CREATE TABLE "parent" (
    "id" TEXT PRIMARY KEY,
    "name" TEXT,
    "remark" TEXT,
    "dlt" INTEGER,
    "createtime" TEXT,
    "updatetime" TEXT
);

CREATE TABLE "child" (
    "id" TEXT PRIMARY KEY,
    "pid" TEXT,
    "name" TEXT,
    "age" INTEGER,
    "score" REAL,   
    "remark" TEXT,
    "dlt" INTEGER,
    "createtime" TEXT,
    "updatetime" TEXT
);

下文假定Parent为M个,每个parent有N个Child。Conn表示数据库连接。

优化二级查询

需求:查询所有Parent的所有Child组成一个Map

方案一


List<Parent> pList=Conn.get("select * from parent");
for(Parent p:pList)

    List<Child> cList=Conn.get("select * from child where pid=?",p.id);
    map.put(p,cList);

此方法通过先查所有的父项目,然后遍历父项目再查询子项目。此种方法简单粗暴,也最容易理解。查询次数:M+1次。

方案二

List<Parent> pList=Conn.get("select * from parent order by pid");
List<Child> cList=Conn.get("select * from child order by pid");
int position=0;
String currentPid="";
List<Child> temp;
for(Child c:cList)

    if(currentPid.equals(c.pid))
    
        temp.add(c)
    else
        temp=new ArrayList<>();
        map.put(pList.get(position++),temp)
        currentPid=c.pid;
        temp.add(c);
    

此方法利用数据库对数据排序,形成有序队列,然后分段截取List组装成Map。显然只进行了两次查询,极大的缓解了查询次数。这里相对于方案一查询语句多了一个排序,sqlite使用B-Tree建立索引,主键百万级数据排序都是毫秒级的速度。方案二的耗时在Parent数量越大时查询速度越优于方案一。

但是方案二是有Bug的,如果一个Parent没有Child将会导致后序关系错乱。

方案二改进版

List<Parent> pList=Conn.get("select * from parent");
List<Child> cList=Conn.get("select * from child order by pid");

HashMap<String,Parent> indexParent=new HashMap<>();
//对Parent建立id<--->Parent
for(Parent p:pList)

    indexParent.put(p.id,p);

String currentPid="";
List<Child> temp;
for(Child c:cList)

    if(currentPid.equals(c.pid))
    
        temp.add(c);
    else
        temp=new ArrayList<>();
        map.put(indexParent.get(currentPid),temp);
        //遍历完一个就从现有集合中移除一个Parent
        indexParent.remove(currentPid);
        currentPid=c.pid;
        temp.add(c);
    

//把剩下没有Child的Parent添加到Map中
Iterator iter = indexParent.entrySet().iterator();
while (iter.hasNext()) 
  Map.Entry entry = (Map.Entry) iter.next();
  Parent p = (Parent)entry.getValue();
  map.put(p,null);

改进版的方法List可以是无序的,因为用Parent.ID对其建立了一个HashMap。最后把没有Child的Parent给加到了结果集里,这段代码可按需添加。

测试:

测试机型:Mate7

无条件查询(指没有额外的Where条件)

数据量:Parent(513),Child(64058)
方案1耗时:48559ms
方案2耗时:15035ms

数据量:Parent(10),Child(10)
方案1耗时:210ms
方案2耗时:163ms

带复杂条件查询

数据量:Parent(513),Child(64058)
Where条件为:child.name like ‘%我%’
条件结果集:Parent(13),Child(13)
方案1耗时:32572ms
方案2耗时:294ms

总结

在小数据集里方案一和方案二区别不是很明显,大数据集的时候很明显速度快了三倍。在带复杂条件查询的时候,方案二的速度十分的快,因为方案二只进行了一次数据比对,而方案一比对了M次。

实时搜索优化

需求:对EditText中输入的信息实时检索。
EditText监听如下:

String currentKey="";
etSearch.addTextChangedListener(new TextWatcher() 
   @Override
   public void beforeTextChanged(CharSequence s, int start, int count, int after) 
   
   @Override
   public void onTextChanged(CharSequence s, int start, int before, int count) 
   
   @Override
   public void afterTextChanged(Editable s) 
        currentKey=s.toString();
        search(currentKey);
   
);

异步数据库搜索

private void search(final String key)

    mExcutorService.execute(new Runnable() 
        @Override
        public void run() 
            List rs=Conn.find(key);
            runOnUiThread(new Runnable() 
                @Override
                public void run() 
                    adapter.setData(rs);
                    adapter.notifyDataSetChange();
                
            );
        
    );

此方法利用线程池来完成搜索,然后将搜索结果post到主线程更新界面。优点很明显,在子线程搜索,搜索过程不会阻塞UI线程。但是实际上用户只关心最后停留文字的搜索结果。

改进版搜索

private void search(final String key)

    mExcutorService.execute(new Runnable() 
        @Override
        public void run() 
            List rs=Conn.find(key);
            synchronized (currentKey)
            
                 //只有当前输入的Key与本次搜索的Key一致才去刷新界面。否则放弃本次搜索结果。
                if(key.eqauls(currentKey))
                
                    runOnUiThread(new Runnable() 
                        @Override
                        public void run() 
                            adapter.setData(rs);
                            adapter.notifyDataSetChange();
                        
                    );
                
            

        
    );

这里改进了结果展示的逻辑,虽然还是每个字段在默默的搜索,但是如果在搜索过程中,用户改变了搜索关键字,那么本次搜索结果就扔掉了。这样减少了界面刷新导致卡顿。

内存里的递归搜索

在实际搜索应用中,当用户输入一个a的时候得到的结果集为ResultA,当用户再输入一个b,则搜索ab得到的结果集为ResultAB。那么一定有关系ResultAB属于ResultA。这样就有了一种新的搜索方案,根据用户的输入行为进行递归搜索,在上一次结果上进行搜索。

List allData;
List currentData;
String currentKey;

public void search(String key)

    //搜索关键字没有改变
    if(key.eqauls(currentKey))
        return;
    if(key.contains(currentKey))
    
        //符合递归搜索前提
        currentData= search(currentData,key);
    else
        currentData= search(allData,key);
    
     currentKey=key;


public List search(List list,String key)

    //搜索比对代码

总结

在实际情况中,大数据量使用sqlite搜索的速度绝对优于普通的遍历内存搜索速度,数据库对数据建立有索引并且搜索的算法比遍历式英明得多。

sqlite使用

Group By

该关键字主要用于对结果集分组。

需求:查询每个Parent下age字段最大的Child。

解决方案:利用group by关键字来筛选每个Parent下面age字段最大的条目

SELECT p.id,p.name,c.id,c.name,c.age
FROM parent p
LEFT JOIN 
    (SELECT * FROM (SELECT * FROM child ORDER BY pid,age ASC)
    GROUP BY pid) c
ON p.id = c.pid
  • 先对Child表排序,pid优先排序,然后age升序排列。
  • 对Child表进行Group By pid,Group by的特性是保留最后一条。因此结果集中就是每个Parent年龄最大的Child。
  • 通过Left Join把Child和Parent连接起来即是想要的结果。

distinct

该关键字的意义是去除重复的行,该关键字可以和其他函数一起使用。例如,函数”count(distinct X)”返回字段X的不重复非空值的个数,而不是字段X的全部非空值。avg(distinct X)、sum(distinct X) 也有相同的效果。

group_concat(x[,y])

该函数返回一个字符串,该字符串将会连接所有非NULL的x值。该函数的y参数将作为每个x值之间的分隔符,如果在调用时忽略该参数,在连接时将使用缺省分隔符”,”。各个字符串之间的连接顺序是不确定的。当你想获得一段数据某一字段用逗号分隔开来的数据,那么你就应该用此函数。

ifnull(x,y)、coalesce(X,Y,…)

coalesce(X,Y,…) 返回第一个非空参数的副本。若所有的参数均为NULL,返回NULL。至少2个参数。 ifnull(X,Y) 返回第一个非空参数的副本。 若两个参数均为NULL,返回NULL。与 coalesce()类似。 常用此函数来过滤掉空值。例如当前值和默认值的选择。

sqlite里面的NULL

  • x||null=NULL
  • null <>’123’=NULL
  • NULL !=NULL ISNULL=1
  • group by时NULL会被当成一类

以上是关于sqlite知识分享的主要内容,如果未能解决你的问题,请参考以下文章

(SQLite3封装) 一行代码实现增删改查 - 值得收藏转发分享

使用jooq上下文将内存sqlite转储到字节[]

python使用上下文管理器实现sqlite3事务机制

简易SQLite3数据库学习

一起学Android之Sqlite

SQLite:如何使用复合键从单个表中选择“每个用户的最新记录”?