查询优化,摆脱子查询
Query optimization, get rid of subqueries
我使用的是 sqlite3,但它的 SQL 支持相当标准,所以只要 SQL 不包含任何专有扩展就应该没问题。我的架构如下:
create table test (
_id integer primary key,
name text,
enabled integer not null default 1
);
create table task (
_id integer primary key,
_test_id integer not null references test on delete cascade,
favorite integer not null default 0,
comment text
);
简而言之:有可能启用或不启用的测试;测试有多个任务,可以收藏,可以评论。
我需要编写的两个最复杂的查询如下:
A select 检索数据库是否包含任何已启用测试的至少 1 个收藏任务和至少 1 个评论任务的信息(即不按测试分组)。我想出了以下怪诞的东西:
select
exists(select task._id from task as task inner join test as test on task._test_id=test._id where task.favorite=1 and test.enabled=1 limit 1) as has_favorite,
exists(select task._id from task as task inner join test as test on task._test_id=test._id where task.comment is not null and test.enabled=1 limit 1) as has_commented;
A select 检索测试核心数据(id、名称等)以及有关其任务计数的信息,测试是否包含至少 1 个收藏任务和至少 1 个评论任务.我想到了这个:
select
test.*,
(select count(*) from task where _test_id=test._id) as task_count,
exists(select _id from task where favorite=1 and _test_id=test._id limit 1) as has_favorite,
exists(select _id from task where comment is not null and _test_id=test._id limit 1) as has_commented
from test as test where test.enabled=1 order by test._id asc
实际上,'has_favorite' 和 'has_commented' 信息并不是唯一的信息,但它们描述了我的疑虑 - 这些查询非常大,包含相当多的子查询(我读过 subselects 对性能不利)和重复。
问题:是否可以更轻松地编写查询?让它们更好、更简洁?不重复这么多?例如,我在想也许有一种方法可以在任务表和测试表之间只执行一个连接,并以某种方式从那里导出数据。
编辑: 所以看来我可以为第一个写这个:
select
count(*) as task_count,
max(task.favorite) as has_favorite,
count(task.comment) as has_commented
from task as task inner join test as test on task._test_id=test._id where test.enabled=1;
这是第二个:
select
test.*,
count(*) as task_count,
max(task.favorite) as has_favorite,
count(task.comment) as has_commented
from task as task inner join test as test on task._test_id=test._id where test.enabled=1 group by test._id;
如果 max(task.favorite) 大于 0,则表示至少有 1 个任务是最喜欢的。我可以用 'sum(task.favorite)' 替换它,如果总和 > 0,则有一个最喜欢的。
这是否比最初的提议(有 exists(subselect))更好?这似乎更容易。
我最终选择了与我编辑中的连接类似的连接,因为它们工作得很好,而且还允许一次性收集其他信息。
我使用的是 sqlite3,但它的 SQL 支持相当标准,所以只要 SQL 不包含任何专有扩展就应该没问题。我的架构如下:
create table test (
_id integer primary key,
name text,
enabled integer not null default 1
);
create table task (
_id integer primary key,
_test_id integer not null references test on delete cascade,
favorite integer not null default 0,
comment text
);
简而言之:有可能启用或不启用的测试;测试有多个任务,可以收藏,可以评论。
我需要编写的两个最复杂的查询如下:
A select 检索数据库是否包含任何已启用测试的至少 1 个收藏任务和至少 1 个评论任务的信息(即不按测试分组)。我想出了以下怪诞的东西:
select exists(select task._id from task as task inner join test as test on task._test_id=test._id where task.favorite=1 and test.enabled=1 limit 1) as has_favorite, exists(select task._id from task as task inner join test as test on task._test_id=test._id where task.comment is not null and test.enabled=1 limit 1) as has_commented;
A select 检索测试核心数据(id、名称等)以及有关其任务计数的信息,测试是否包含至少 1 个收藏任务和至少 1 个评论任务.我想到了这个:
select test.*, (select count(*) from task where _test_id=test._id) as task_count, exists(select _id from task where favorite=1 and _test_id=test._id limit 1) as has_favorite, exists(select _id from task where comment is not null and _test_id=test._id limit 1) as has_commented from test as test where test.enabled=1 order by test._id asc
实际上,'has_favorite' 和 'has_commented' 信息并不是唯一的信息,但它们描述了我的疑虑 - 这些查询非常大,包含相当多的子查询(我读过 subselects 对性能不利)和重复。
问题:是否可以更轻松地编写查询?让它们更好、更简洁?不重复这么多?例如,我在想也许有一种方法可以在任务表和测试表之间只执行一个连接,并以某种方式从那里导出数据。
编辑: 所以看来我可以为第一个写这个:
select
count(*) as task_count,
max(task.favorite) as has_favorite,
count(task.comment) as has_commented
from task as task inner join test as test on task._test_id=test._id where test.enabled=1;
这是第二个:
select
test.*,
count(*) as task_count,
max(task.favorite) as has_favorite,
count(task.comment) as has_commented
from task as task inner join test as test on task._test_id=test._id where test.enabled=1 group by test._id;
如果 max(task.favorite) 大于 0,则表示至少有 1 个任务是最喜欢的。我可以用 'sum(task.favorite)' 替换它,如果总和 > 0,则有一个最喜欢的。
这是否比最初的提议(有 exists(subselect))更好?这似乎更容易。
我最终选择了与我编辑中的连接类似的连接,因为它们工作得很好,而且还允许一次性收集其他信息。