我有一些MySQL视图,它们根据一些相对简单的子查询定义了许多额外的列。该数据库也是多租户的,因此每一行都有一个公司ID。
我遇到的问题是我的视图会在被公司ID过滤之前对每一行进行评估,从而产生巨大的性能问题。是否有任何方法可以懒惰地评估视图,因此外部查询中的“where”子句适用于视图中的子查询。或者是否有类似于我可用于添加额外字段的视图。我想在SQL中计算它们,因此计算的字段可用于过滤/搜索/排序/分页。
我已经看过解释可用算法的MySQL文档,并且我知道这些视图不能被视为“合并”,因为它们包含子查询。
查看
create view companies_view as
select *,
(
select count(id) from company_user where company_user.company_id = companies.id
) as user_count,
(
select count(company_user.user_id)
from company_user join users on company_user.user_id = users.id
where company_user.company_id = companies.id
and users.active = 1
) as active_user_count,
(
select count(company_user.user_id)
from company_user join users on company_user.user_id = users.id
where company_user.company_id = companies.id
and users.active = 0
as inactive_user_count
from companies;
查询
select * from companies_view where company_id = 123;
我希望在从主查询范围应用'where company_id = 123'之后评估视图中的子查询。我无法在视图中对公司ID进行硬编码,因为我希望视图可用于任何公司ID。
答案 0 :(得分:2)
您无法更改由MySQL服务器设置的评估顺序。
但是,在这种特殊情况下,您可以重写整个sql语句以使用连接和条件计数而不是子查询:
select c.*,
count(u.id) as user_count,
count(if(u.active=1, 1, null)) as active_user_count,
count(if(u.active=0, 1, null)) as inactive_user_count
from companies c
left join company_user cu on c.id=cu.company_id
left join users u on cu.user_id = u.id
group by c.company_id, ...
如果你有MySQL v5.7,那么你可能不需要在group by子句中添加任何其他字段,因为companies
表中的其他字段在功能上依赖于company_id。在早期版本中,您可能必须列出companies
表中的所有字段(取决于sql模式设置)。
优化此类查询的另一种方法是使用非规范化。您的users
和company_user
表可能比companies
表的记录多得多。您可以在user_count
表中添加active_user_count
,inactive_user_count
和companies
字段,在company_user
表之后添加插入/更新/删除触发器并在更新users
表后更新这两个字段。这样您就不需要在视图中进行连接和条件计数。
答案 1 :(得分:1)
可能说服优化器使用MERGE
算法处理带标量子查询的视图......你只需要在自己的游戏中击败优化器。
对于某些人来说,这似乎是非常不正统的,但这是我在成功需要时使用的模式。
创建一个存储函数来封装每个子查询,然后在视图中引用存储的函数。优化器仍然没有意识到函数将调用子查询。
CREATE FUNCTION user_count (_cid INT) RETURNS INT
DETERMINISTIC
READS SQL DATA
RETURN (SELECT count(id) FROM company_user WHERE company_user.company_id = _cid);
请注意,带有单个语句的存储函数不需要BEGIN
/ END
或更改DELIMITER
。
然后在视图中,将子查询替换为:
user_count(id) AS user_count,
并为每个子查询重复此过程。
然后,优化器将视图作为MERGE
视图处理,根据外部WHERE
从公司表中选择一个适当的行,调用函数,然后...解决问题。< / p>