【问题标题】:sqlite: insert or update a row, performance issuesqlite:插入或更新一行,性能问题
【发布时间】:2013-11-22 05:25:26
【问题描述】:

sqlite 表:

CREATE TABLE IF NOT EXISTS INFO
(
    uri TEXT PRIMARY KEY,
    cap INTEGER,
    /* some other columns */
    uid TEXT
);

INFO 表有 5K+ 行,在低功耗设备上运行(相当于 3 年的手机)。 我有这个任务:将带有一些值的新 URI 插入到 INFO 表中,但是,如果 URI 已经存在,那么我需要通过向它附加额外的文本来更新 uid 文本字段 if在现有的uid 字符串中找不到要附加的额外文本;所有其他字段应保持不变。

例如:INFO 已经有uri="http://example.com" 和这个uid 字符串:"|123||abc||xxx|"。 我需要添加uri="http://example.com" 和uid="|abc|"。由于“|abc|”是 uri 现有字段中的子字符串,则不应更新任何内容。无论如何,不​​应更新剩余字段

要完成它,我有以下选择:

  1. 构建一些 sql 查询(如果可以在一个 sql 语句中使用 sqlite 执行类似的操作),
  2. 分两步手动执行所有操作:a) 检索 uid 行,手动执行所有处理,b) 更新现有行或在需要时插入新行

考虑到这是受限设备,哪种方式更可取?如果我省略了子字符串匹配的额外要求并始终将 uid 附加到现有的 uid 字段怎么办?

【问题讨论】:

  • 我在我的答案中添加了一些性能测试 - 确认运行单独的语句对于 sqlite 来说会更快。由于它在进程内运行,因此开销非常小。

标签: sqlite


【解决方案1】:

“如果可以在一条 sql 语句中使用 sqlite”:

是的,这是可能的。 “UPSERT”声明已在此 question 中进行了很好的讨论。

应用于您的扩展案例,您可以在一个语句中这样做:

insert or replace into info  (uri, cap, uid) 
values ( 'http://example.com', 
         coalesce((select cap  from info where uri  = 'http://example.com'),'new cap'),
         (select case 
             when (select uid 
                   from  info 
                   where uri  = 'http://example.com') is null 
             then '|abc|'
             when instr( (select uid 
                          from  info 
                          where uri  = 'http://example.com'),'|abc|') = 0 
             then (select uid 
                   from info 
                   where uri  = 'http://example.com') || '|abc|'
             else (select uid 
                  from  info 
                  where uri  = 'http://example.com')
             end )
      );

检查 EXPLAIN QUERY PLAN 给我们的信息

selectid    order       from        detail
----------  ----------  ----------  -------------------------
0           0           0           EXECUTE SCALAR SUBQUERY 0
0           0           0           SEARCH TABLE info USING INDEX sqlite_autoindex_INFO_1 (uri=?)
0           0           0           EXECUTE SCALAR SUBQUERY 1
1           0           0           EXECUTE SCALAR SUBQUERY 2
2           0           0           SEARCH TABLE info USING INDEX sqlite_autoindex_INFO_1 (uri=?)
1           0           0           EXECUTE SCALAR SUBQUERY 3
3           0           0           SEARCH TABLE info USING INDEX sqlite_autoindex_INFO_1 (uri=?)
1           0           0           EXECUTE SCALAR SUBQUERY 4
4           0           0           SEARCH TABLE info USING INDEX sqlite_autoindex_INFO_1 (uri=?)
1           0           0           EXECUTE SCALAR SUBQUERY 5
5           0           0           SEARCH TABLE info USING INDEX sqlite_autoindex_INFO_1 (uri=?)

据我所知,sqlite 不会缓存标量子查询的结果(在查看 EXPLAIN 为上述语句生成的 VM 代码时,我找不到任何 chaching 的证据)。因此,由于 sqlite 是一个进程内数据库,因此使用两个单独的语句执行操作很可能会比这更好。

您可能希望为此对运行时进行基准测试 - 结果当然取决于您的主机语言和您使用的接口(C、JDBC、ODBC...)。

编辑

使用 JDBC 驱动程序和 sqlite 3.7.2 进行的小性能基准测试,对 table info 中 5000 行的基本数据集运行 100.000 次修改,50% 更新,50% 插入,证实了上述结论:

  1. 使用三个准备好的语句(首先是选择,然后是更新或插入,具体取决于所选数据):702ms
  2. 使用上面的组合语句:1802ms

运行时在多次运行中相当稳定。

【讨论】:

  • 谢谢,我也决定采用2条s​​ql查询方式,否则维护这样的查询相当复杂
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
  • 2013-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多