【问题标题】:SQLite takes too much time on using OR condition with WHERE clauseSQLite 在 WHERE 子句中使用 OR 条件花费了太多时间
【发布时间】:2019-12-22 12:00:25
【问题描述】:

我有一个 1 亿行的 SQLite 数据库(表)。

表架构:

CREATE TABLE users
(
    u_id      VARCHAR(32) PRIMARY KEY,
    u_status  BOOLEAN,
    u_country VARCHAR(2),
    u_score   INT
);

CREATE INDEX dex_sta ON users (u_status);
CREATE INDEX dex_cou ON users (u_country);
CREATE INDEX dex_sco ON users (u_score);

CREATE INDEX dex_sns ON users (u_status, u_score);
CREATE INDEX dex_mul ON users (u_status, u_country, u_score);

当我使用以下这些简单查询而不选择多个国家/地区时,我会在 15 毫秒内得到响应。

SELECT * FROM users WHERE u_status = 1 AND u_country = 'US' ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

SELECT * FROM users WHERE u_status = 1 AND u_country = 'IN' ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

问题从以下查询开始

当我尝试使用 OR 条件匹配多个国家/地区时,查询需要 60 秒 来响应。

SELECT * FROM users WHERE u_status = 1 AND (u_country = 'US' OR u_country = 'IN') ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

查询改进 1:

当我通过 (INDEXED BY) 强制查询使用特定索引时,它会在 1 秒

内做出响应
SELECT * FROM users INDEXED BY dex_mul WHERE u_status = 1 AND (u_country = 'US' OR u_country = 'IN') ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

查询改进 2:

当我使用 UNION ALL 查询时,响应需要 500 毫秒

SELECT * FROM users WHERE u_status = 1 AND u_country = 'US'
UNION ALL
SELECT * FROM users WHERE u_status = 1 AND u_country = 'IN'
ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

是否有可能在 内获得响应,例如匹配多个国家/地区的第一个查询?

【问题讨论】:

  • 请发布查询的解释计划。
  • LIMIT 10 OFFSET 100000; 那么,就像从100000th 整个数据集中的最大元素开始?
  • 您的dex_mul 索引使dex_sta 无用,顺便说一句,因为后者是前者的前缀。有关详细信息,请参阅sqlite.org/queryplanner.html#_multi_column_indices。 (dex_coudex_sco 也不太可能在这些查询中使用;不过请用EXPLAIN QUERY PLAN 确认)
  • 你也应该尽量避免使用大的OFFSET。请参阅 use-the-index-luke.com/sql/partial-results 进行讨论和替代方案,具体取决于您正在做什么。
  • 最后,如果您还没有这样做,在桌面上运行ANALYZE 可能会有所帮助。至少,查询规划器将能够更好地决定使用哪一个索引。

标签: sqlite select where-clause


【解决方案1】:

在查询中使用OR 条件不允许使用索引。您可以将查询转换为 UNION ALL 子句 -

SELECT *
FROM users
WHERE u_status = 1 AND u_country = 'US'
UNION ALL
SELECT *
FROM users
WHERE u_status = 1 AND u_country = 'IN'
ORDER BY u_score DESC LIMIT 10 OFFSET 100000;

这可能会解决您的问题。

【讨论】:

    猜你喜欢
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    • 2020-07-23
    • 2013-07-11
    • 2014-01-13
    相关资源
    最近更新 更多