【问题标题】:SQLite Query is slow on iOS, and I have no clue whyiOS 上的 SQLite 查询很慢,我不知道为什么
【发布时间】:2014-01-23 15:23:21
【问题描述】:

我有一个可以离线使用的应用程序,它可以从本地和远程源对数据库进行更改。我有一个指标查询,每次写入数据库时​​都需要运行(实时更新仪表板),由于它可以在本地或远程完成,它可能应该由数据库触发。

基本上,每次事务完成时,我都会运行以下 SQL:

- (NSString *)selectDashboardActivities {
    return [NSString stringWithFormat:@"SELECT a.*, f.*, p.*, r.*, phys.*, account.*, t.*, s.*, t_i.*, a_i.*, c.*, consent.*, therapy.*, indication.*,"
                                                  " (SELECT CASE"
                                                  "  WHEN ar.resource_cust_num = user.custno THEN %d"
                                                  "  WHEN aardt.territory IS NOT NULL        THEN %d"
                                                  "  WHEN aardt.territory IS NULL            THEN %d"
                                                  "  ElSE %d"
                                                  " END) AS file_scope"
                                                  " FROM PT_ACTIVITY a"
                                                  " CROSS JOIN PT_ACTIVITY_TYPE_STATUS  s          ON  a.activity_type_status_id    = s.activity_type_status_id"
                                                  "                                                AND s.active_flag                = 'Y'"
                                                  "                                                AND s.closed_flag                IN %@"
                                                  " CROSS JOIN PT_ACTIVITY_TYPE         t          ON  a.activity_type_id           = t.activity_type_id"
                                                  "                                                AND t.active_flag                = 'Y'"
                                                  " CROSS JOIN PT_ACTIVITY_RESOURCE     ar         ON  a.activity_id                = ar.activity_id"
                                                  "                                                AND ar.active_flag               = 'Y'"
                                                  " CROSS JOIN PT_FILE                  f          ON  a.file_id                    = f.file_id"
                                                  "                                                AND f.active_flag                = 'Y'"
                                                  " CROSS JOIN PT_PATIENT               p          ON  f.patient_id                 = p.patient_id"
                                                  "                                                AND p.active_flag                = 'Y'"
                                                  " CROSS JOIN PT_RESOURCE              r          ON  ar.resource_cust_num         = r.sold_to_cust_num"
                                                  "                                                AND r.active_flag                = 'Y'"
                                                  " CROSS JOIN PT_AARDT                 aardt      ON  a.rdt_key                    = aardt.rdt_key"
                                                  "                                                AND aardt.active_flag            = 'Y'"
                                                  " CROSS JOIN PT_THERAPY               therapy    ON  f.therapy_id                 = therapy.therapy_id"
                                                  "                                                AND therapy.active_flag          = 'Y'"
                                                  " CROSS JOIN PT_ICON                  a_i        ON  t.icon_id                    = a_i.icon_id"
                                                  "                                                AND a_i.active_flag              = 'Y'"
                                                  " CROSS JOIN PT_ICON                  t_i        ON  therapy.icon_id              = t_i.icon_id"
                                                  "                                                AND t_i.active_flag              = 'Y'"
                                                  " CROSS JOIN PT_USER                  user       ON  user.user_id                 = %@"
                                                  "                                                AND user.active_flag             = 'Y'"
                                                  " LEFT  JOIN PT_ACTIVITY_CONTACT_TYPE act        ON  a.activity_type_id           = act.activity_type_id"
                                                  "                                                AND act.primary_flag             = 'Y'"
                                                  "                                                AND act.active_flag              = 'Y'"
                                                  " LEFT  JOIN PT_ACTIVITY_CONTACT      ac         ON  a.activity_id                = ac.activity_id"
                                                  "                                                AND ac.activity_contact_type_id  = act.activity_contact_type_id"
                                                  "                                                AND ac.active_flag               = 'Y'"
                                                  " LEFT  JOIN PT_PHYSICIAN             phys       ON  ac.contact_no                = phys.contact_no"
                                                  "                                                AND phys.active_flag             = 'Y'"
                                                  " LEFT  JOIN PT_ACCOUNT               account    ON  a.sold_to_cust_num           = account.sold_to_cust_num"
                                                  "                                                AND account.sold_to_cust_num     = account.ship_to_cust_num"
                                                  "                                                AND account.active_flag          = 'Y'"
                                                  " LEFT  JOIN PT_FILE_CONSIDERATION    fc         ON  f.file_id                    = fc.file_id"
                                                  "                                                AND fc.active_flag               = 'Y'"
                                                  " LEFT  JOIN PT_CONSIDERATION         c          ON  fc.consideration_id          = c.consideration_id"
                                                  "                                                AND c.active_flag                = 'Y'"
                                                  " LEFT  JOIN PT_CONSENT               consent    ON  p.patient_id                 = consent.patient_id"
                                                  "                                                AND consent.active_flag          = 'Y'"
                                                  " LEFT  JOIN PT_FILE_INDICATION       fi         ON  f.file_id                    = fi.file_id"
                                                  "                                                AND fi.active_flag               = 'Y'"
                                                  " LEFT  JOIN PT_INDICATION            indication ON  indication.indication_id     = fi.indication_id"
                                                  "                                                AND indication.active_flag       = 'Y'"
                                                  " WHERE date(max(a.activity_date, ifnull(a.client_updated_date, a.activity_date)), 'unixepoch') > date('now', 'unixepoch', '-120 day')"
                                                  " AND a.active_flag = 'Y'"
                                                  " AND ar.resource_cust_num IN %@",
                                                 FileScopeMyFile,
                                                 FileScopeTerritory,
                                                 FileScopeDistrict,
                                                 FileScopeUnknown,
                                                 self.filter.closedFlagString,
                                                 [MDTAuthenticationManager authToken].userIdNumber,
                                                 self.filter.resourcesString
    ];
}

FileScope 参数是枚举,其余的是字符串。 self.filter.resourcesString 可以是类似(1,2,3,4,5,6) 的字符串,目前可以包含 1-1000 个资源。使用 1 个资源运行会更快,但并不显着。

在 SQLite 上运行 explain query plan 给了我这个:

0| 0| 0|SEARCH TABLE PT_ACTIVITY AS a USING INDEX PT_ACTIVITY_IDX7 (ACTIVE_FLAG=?) (~1049 rows)
0| 1| 1|SEARCH TABLE PT_ACTIVITY_TYPE_STATUS AS s USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0| 0| 0|EXECUTE LIST SUBQUERY 1
0| 2| 2|SEARCH TABLE PT_ACTIVITY_TYPE AS t USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0| 3| 3|SEARCH TABLE PT_ACTIVITY_RESOURCE AS ar USING INDEX PT_ACTIVITY_RESOURCE_IDX1 (ACTIVITY_ID=?) (~2 rows)
0| 0| 0|EXECUTE LIST SUBQUERY 1
0| 4| 4|SEARCH TABLE PT_FILE AS f USING INDEX sqlite_autoindex_PT_FILE_1 (FILE_ID=?) (~1 rows)
0| 5| 5|SEARCH TABLE PT_PATIENT AS p USING INDEX sqlite_autoindex_PT_PATIENT_1 (PATIENT_ID=?) (~1 rows)
0| 6| 6|SEARCH TABLE PT_RESOURCE AS r USING INDEX sqlite_autoindex_PT_RESOURCE_1 (SOLD_TO_CUST_NUM=?) (~1 rows)
0| 7| 7|SEARCH TABLE PT_AARDT AS aardt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0| 8| 8|SEARCH TABLE PT_THERAPY AS therapy USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0| 9| 9|SEARCH TABLE PT_ICON AS a_i USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|10|10|SEARCH TABLE PT_ICON AS t_i USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|11|11|SEARCH TABLE PT_USER AS user USING AUTOMATIC COVERING INDEX (USER_ID=? AND ACTIVE_FLAG=?) (~7 rows)
0|12|12|SEARCH TABLE PT_ACTIVITY_CONTACT_TYPE AS act USING INDEX PT_ACTIVITY_CONTACT_TYPE_ACTIVITY_TYPE_ID (ACTIVITY_TYPE_ID=?) (~2 rows)
0|13|13|SEARCH TABLE PT_ACTIVITY_CONTACT AS ac USING INDEX PT_ACTIVITY_CONTACT_PK (ACTIVITY_ID=? AND ACTIVITY_CONTACT_TYPE_ID=?) (~1 rows)
0|14|14|SEARCH TABLE PT_PHYSICIAN AS phys USING INDEX PT_PHYSICIAN_IDX5 (CONTACT_NO=?) (~2 rows)
0|15|15|SEARCH TABLE PT_ACCOUNT AS account USING INDEX PT_ACCOUNT_IDX2 (SOLD_TO_CUST_NUM=?) (~2 rows)
0|16|16|SEARCH TABLE PT_FILE_CONSIDERATION AS fc USING INDEX PT_FILE_CONSIDERATION_FILE_ID (FILE_ID=?) (~2 rows)
0|17|17|SEARCH TABLE PT_CONSIDERATION AS c USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|18|18|SEARCH TABLE PT_CONSENT AS consent USING INDEX PT_CONSENT_IDX1 (PATIENT_ID=?) (~2 rows)
0|19|19|SEARCH TABLE PT_FILE_INDICATION AS fi USING INDEX PT_FILE_INDICATION_FILE_ID (FILE_ID=?) (~2 rows)
0|20|20|SEARCH TABLE PT_INDICATION AS indication USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0| 0| 0|EXECUTE CORRELATED SCALAR SUBQUERY 1

如果我了解查询计划,则应该有适当的索引。

在 iOS 设备上,此查询需要 8-12 秒。我需要它花费少于 1 秒,最好少于 0.5 秒。我确实需要所有这些表来生成正确的仪表板项目。第一个表 PT_ACTIVITY 有 4500 个活动。这些表格是按照我实验发现的最快顺序排列的。

对于如何加快查询速度,有人有什么建议吗?我的想法和理智都快用完了...在此先感谢!

【问题讨论】:

  • 您在一个查询中加入 20 个表,但您想知道为什么速度很慢?
  • @rmaddy 这很粗鲁。为了满足业务需求,我别无选择。
  • 这不是粗鲁的。这只是一个简单的事实,在移动设备上运行的 20 表连接只能做到如此之快。这完全取决于每个表中有多少行。再加上你的 where 子句正在做日期计算,这也总是很慢。
  • @rmaddy 我可以尝试以 MongoDB 风格的方法进行非规范化,但这需要我在远程同步格式和我们的本地非规范化数据库之间进行转换(和返回)的整个过程。我希望尽可能避免这种情况。

标签: ios sql sqlite


【解决方案1】:

如果不对数据库进行一些实验,就很难诊断出性能问题的确切来源,但是查看该 SQL 会有一些反应:

  1. 当然,表的数量可能是问题所在。您可能想暂时尝试减少您加入的表的数量,看看这是否会显着改变性能。如果是这样,您可以使用多个 SELECT 语句检索您的数据。

  2. 您还返回了大量数据(所有表中的所有列)。我们不知道这些表中有什么,但您可能会考虑只返回您需要的列。

    值得注意的是,如果您有任何 BLOB 列,则会严重影响性能。

  3. 您的所有联接都是外部联接或交叉联接。您不使用内部联接的任何原因?

    • 您真的需要执行所有这些外连接(众所周知,这些外连接效率低下;它们中的任何一个都可以用内连接代替)吗?

    • 所有这些交叉连接都很奇怪。为什么要交叉连接?

  4. 鉴于我们不了解您的数据模型,因此我不愿这么说,但您可能需要仔细考虑检索数据的顺序。例如,根据您的计划,看起来PT_ACTIVITY_RESOURCE(由WHERE 子句中的ar.resource_cust_num 过滤)可能是SELECT 的更好表,然后从那里开始进行连接。如果不知道您在所有这些不同的表中有多少条记录,很难说,但您可能应该首先选择最严格定义结果集的表,然后从那里加入。

    顺便说一句,如果您保持原样,您可能希望将ar.resource_cust_num IN %@WHERE 子句移到JOIN ... ON ... 子句中。这可能会对性能产生重大影响。

  5. 我会尝试暂时删除file_scope 子句或简化WHERE 子句中的日期逻辑,看看其中任何一个是否会导致任何奇怪的意外性能损失。

    李>

根据所提供的信息很难诊断出这种情况,但这些都是需要考虑的事情。但就基本策略而言,我倾向于慢慢开始简化 SQL,看看您是否能确定性能下降的确切来源。

【讨论】:

  • 这些都是值得考虑的要点。我使用cross join 的原因是因为我认为它们完成了与joininner join 相同的行为,但它像left join 一样强制执行命令。在我最初的尝试中,允许查询优化器完成它的工作并没有奏效,但也许自从更新了我的版本并使用了analyze 命令之后,情况可能会有所不同。
  • 我建议现在恢复到内部联接。正如 SQLite 文档所说,“避免使用 CROSS JOIN,除非在需要手动控制查询优化器的特定情况下。避免在应用程序开发的早期使用 CROSS JOIN,因为这样做是过早的优化。CROSS JOIN 的特殊处理是 SQLite 特有的功能,不是标准 SQL 的一部分。”
  • 我去看看会发生什么:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-06
  • 2019-09-07
  • 2022-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多