我有一个支持离线的应用程序,并从本地和远程源进行数据库更改。我每次写入数据库时都需要运行一个度量查询(仪表板的实时更新),并且因为它可以在本地或远程完成,所以它可能应该由数据库触发。
基本上,每次事务完成时,我都运行以下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个活动。这些表格按实验发现的最快订单排序。
有没有人对如何加快此查询有任何建议?我的想法和理智都用完了......先谢谢!
答案 0 :(得分:0)
如果不对数据库进行一些实验,很难诊断出性能问题的确切来源,但是在查看SQL时会有一些反应:
当然表的数量可能是问题所在。您可能希望暂时尝试减少加入的表的数量,并查看是否会显着改变性能。如果是这样,您可以使用多个SELECT
语句检索数据。
您也会返回大量数据(所有表中的所有列)。我们不知道这些表中有什么,但您可能只考虑返回所需的列。
值得注意的是,如果您有任何BLOB
列,则会严重影响效果。
所有连接都是外连接或交叉连接。你不使用内连接的原因是什么?
你真的需要做所有这些外连接(这是非常低效的;它们中的任何一个都可以用内连接替换)吗?
所有这些交叉连接都很好奇。为什么交叉加入?
由于我们不了解您的数据模型,我不愿意这样说,但您可能需要仔细考虑检索数据的顺序。例如,根据您的计划,看起来PT_ACTIVITY_RESOURCE
(由ar.resource_cust_num
子句中的WHERE
过滤)可能是SELECT
更好的表格,并且然后从那里开始做你的联接。如果不知道你在所有这些表中有多少记录,很难说,但你应该首先选择最简单定义结果集并从那里加入的任何表。
顺便说一句,如果你保持原样,你可能希望将ar.resource_cust_num IN %@
从WHERE
子句移到JOIN ... ON ...
子句中。这可能会对绩效产生重大影响。
我会尝试暂时删除file_scope
子句或简化WHERE
子句中的日期逻辑,看看其中任何一个是否会导致任何奇怪的意外性能损失。
根据所提供的信息进行诊断真的很难,但这些是需要考虑的事情。但就基本策略而言,我倾向于慢慢开始简化SQL,看看你是否可以确定性能影响源的确切来源。