SQLite Query在iOS上很慢,我不知道为什么

时间:2014-01-23 15:23:22

标签: ios sql sqlite

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

基本上,每次事务完成时,我都运行以下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个活动。这些表格按实验发现的最快订单排序。

有没有人对如何加快此查询有任何建议?我的想法和理智都用完了......先谢谢!

1 个答案:

答案 0 :(得分:0)

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

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

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

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

  3. 所有连接都是外连接或交叉连接。你不使用内连接的原因是什么?

    • 你真的需要做所有这些外连接(这是非常低效的;它们中的任何一个都可以用内连接替换)吗?

    • 所有这些交叉连接都很好奇。为什么交叉加入?

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

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

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

  6. 根据所提供的信息进行诊断真的很难,但这些是需要考虑的事情。但就基本策略而言,我倾向于慢慢开始简化SQL,看看你是否可以确定性能影响源的确切来源。