【问题标题】:Simple SQLite query taking too long to complete简单的 SQLite 查询需要很长时间才能完成
【发布时间】:2013-09-24 19:10:29
【问题描述】:

我有一个非常简单的查询,大约需要 4 秒才能完成:

SELECT MAX(Date), Bond_Id, Sell_Price FROM Quotes GROUP BY Bond_Id;

该表也很简单,只有大约 31K 条记录。这是架构:

创建表行情( _id 整数主键自动增量, Bond_Id 整数非空, 日期文本不为空, Buy_Yield REAL NOT NULL, Sell_Yield REAL NOT NULL, Buy_Price REAL NOT NULL, Sell_Price REAL NOT NULL, Base_Price REAL NOT NULL, FOREIGN KEY (Bond_Id) REFERENCES Bonds(_id)); CREATE INDEX QuotesNdx ON Quotes(Bond_Id);

我研究了 SQLite 文档,并设法将查询时间从 7s 减少到 4s,这仍然是不可接受的。这几天我一直在绞尽脑汁,没有运气。我已经尝试过 ANALYZE,一些额外的复合索引,并且还尝试删除外键。什么都没有。

这里是解释输出:

0 Trace 0 0 0 解释 select max(date),bond_id,sell_price 从报价组按bond_id; 00 1 午饭 0 0 0 00 2 整数 0 6 0 00 3 整数 0 5 0 00 4 转到 0 20 0 00 5 整数 1 6 0 00 6 返回 0 0 0 00 7 IfPos 5 9 0 00 8 返回 0 0 0 00 9 AggFinal 1 1 0 最大(1) 00 10 复印 1 9 0 00 11 复制 2 10 0 00 12 复印 3 11 0 00 13 结果行 9 3 0 00 14 返回 0 0 0 00 15 空 0 2 0 00 16 空 0 3 0 00 17 空 0 4 0 00 18 空 0 1 0 00 19 返回 0 0 0 00 20 戈苏 0 15 0 00 21 转到 0 48 0 00 22 组数列 0 7 0 00 23 打开读取 0 6 0 00 24 组数列 0 2 0 00 25 打开读取 2 7 0 密钥信息(1,二进制) 00 26 倒带 2 44 13 0 00 27 午饭 2 -7 13 0 01 28 IdxRowid 2 16 0 00 29 移动锗 0 0 16 00 30 列 2 0 8 00 31 等式 7 36 8 collseq(BINARY) 10 32 移动 8 7 0 00 33 戈苏 0 7 0 00 34 IfPos 6 47 0 00 35 戈苏 0 15 0 00 36 列 0 2 17 00 37 CollSeq 0 0 0 collseq(BINARY) 00 38 AggStep 0 17 1 最大(1) 01 39 复印 7 2 0 00 40 列 0 6 3 00 41 真实亲和 3 0 0 00 42 整数 1 5 0 00 43 下一个 2 27 0 00 44 关闭 0 0 0 00 45 关闭 2 0 0 00 46 戈苏 0 7 0 00 47 暂停 0 0 0 00 48 交易 0 0 0 00 49 验证Cookie 0 9 0 00 50 表锁 0 6 0 行情 00 51 转到 0 22 0 00

有什么建议吗?

【问题讨论】:

  • EXPLAIN QUERY PLAN 输出是什么?
  • Date 列设为 LONG 并将日期存储为 EPOCH(与 java Date.getTime() 兼容)格式,它们 MAX(Data) 应该花费更少的时间...
  • 您应该将您的解决方案添加为您问题的答案,然后接受它作为正确答案。不要在问题中添加解决方案。

标签: android sqlite


【解决方案1】:

可以通过创建covering index 来优化此特定查询;列必须按照它们用于查找的顺序:

CREATE INDEX whatever ON Quotes(Bond_ID, Date, Sell_Price);

【讨论】:

    【解决方案2】:

    谢谢大家的回答。实际上,我的查询中的违规者是“GROUP BY”。我通过阅读 SQLite 的 SELECT (http://sqlite.org/lang_select.html) 文档中的这段特定段落,设法找到了解决方案:

    “如果 SELECT 语句是带有 GROUP BY 子句的聚合查询,那么每个指定为 GROUP BY 子句一部分的表达式都会针对数据集的每一行进行评估。然后将每一行分配给一个“组”基于结果;对 GROUP BY 表达式求值结果相同的行分配给同一组。为了对行进行分组,NULL 值被认为是相等的。选择排序规则的常用规则比较文本值适用于评估 GROUP BY 子句中的表达式。GROUP BY 子句中的表达式不必是出现在结果中的表达式。GROUP BY 子句中的表达式可能不是聚合表达式。

    因此,解决方案是创建一个包含 (Date, Bond_Id) 的复合索引,并将我的查询替换为:

    SELECT Date, Bond_Id, Sell_Price FROM Quotes
       WHERE Bond_Id=Bonds._id
       AND Date=(SELECT MAX(Date) FROM Quotes);
    

    现在这个查询不到 1 秒就可以完成,太棒了!

    【讨论】:

    • 只要这是你真正想要的。它不会给出过期债券的价格(因为它们在数据库中没有最后日期的价格)。您可能确实希望原始答案有所变化,但您也需要一个包含日期的索引(因为您想要最大值)。尝试添加一个组合的 (bond_id, date) 索引。
    【解决方案3】:

    您的查询

    SELECT MAX(Date), Bond_Id, Sell_Price FROM Quotes GROUP BY Bond_Id;

    第一: 您的查询不正确。您不应使用保留字作为字段名称。在你的情况下是一个字段“日期”

    如果您在 GROUP BY 中使用任何字段,您还应该将 SELECT 中的所有其他字段与任何分组函数(MIN/MAX/COUNT/etc..)一起使用。

    正确的查询应该是:

    SELECT MAX(Date), Bond_Id FROM Quotes GROUP BY Bond_Id;

    SELECT Bond_Id, MAX(Sell_Price) FROM Quotes GROUP BY Bond_Id HAVING "Date" = MAX("Date");

    第二次

    您需要为 MIN/MAX/... 和 GROUP BY 中使用的每个字段创建索引

    【讨论】:

    • P.S.日期类型的文本也不好。将其替换为: (QuoteDate INTEGER) 并将日期存储为 UNIX-Time 格式。 en.wikipedia.org/wiki/Unix_time
    • DATE 不是reserved word。 SQLite 允许使用没有聚合函数的列。 SQLite 每个表不能使用多个索引。
    猜你喜欢
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    相关资源
    最近更新 更多