查询/数据库优化:如何优化? (我应该使用物化视图吗?)
Posted
技术标签:
【中文标题】查询/数据库优化:如何优化? (我应该使用物化视图吗?)【英文标题】:Query/Database Optimization: How to optimize this? (and should I use a materialized view?) 【发布时间】:2009-09-30 08:01:37 【问题描述】:我有一个关于如何优化查询的问题。实际上,由于我要经常运行查询,我正在考虑使用物化视图或索引视图(这是一个好主意吗?)或非规范化。
考虑以下四个表(省略不相关的字段):
用户(int userId) 组(int groupId) GroupMemberships(int userId、int groupId、bool isSharing) 计算机(int userId)关系是一个用户可以拥有 0..n 台计算机(一个用户对多台计算机)并且可以是 0..n 个组的成员。一个组可以有 0..n 个用户(许多用户对许多组)。 “isSharing”表示用户是在共享到该组还是该组的“只读”成员(即,可以看到共享成员的计算机,但不能共享她自己的)。
查询是为给定用户查找用户可以看到的计算机。用户可以看到她自己的所有计算机。她还可以查看属于她的成员并与该组共享的组中的其他用户的任何计算机。好吧,这没有多大意义,所以这是 O(n^3) psudocode 中的目标:
List<Computer> l
foreach(Computer c in Computers)
if(c.userId == current_user_id)
add c to l
else
foreach(GroupMembership m where m.userId == current_user_id)
foreach(GroupMembership m2 where c.userId == m2.userId && m.groupId == m2.groupId)
if(m2.isSharing)
add c to l
现在我正在使用 ORM 映射器并基本上执行上述操作(我对整个 SQL 的事情不太擅长),但这显然是一个不太理想的解决方案。我在此处列出的每个字段(isShared 除外)上都有索引,在 GroupMembership 的 (userId, groupId) 元组上有一个额外的索引。但是那里的任何数据库向导都可以想到更好的解决方案吗?
该项目尚未上线,但我猜每个用户平均可能有 1.2 台计算机(每个人都有一台,少数人可能有更多),每个用户可能有 0.75 个组成员资格(许多用户不会使用组功能,但这样做的人可能是多个组的成员)。此外,所有这些关联的表都将频繁添加,这可能会使物化视图成为不太实用的解决方案。我正在使用 SQL Server 2008。
谢谢, 祝一切顺利, 罗伯特
【问题讨论】:
【参考方案1】:我认为这可以在没有任何子查询的情况下完成。免责声明:这是我的想法,未经测试。
select distinct computerId
from groupMemberships m1
join groupMemberships m2 on m2.groupId=m1.groupId
and (m2.isSharing or m2.userId=m1.userId)
join computers c on c.userId=m2.userId
where m1.userId=?
除非您希望将这些表中的其他数据包含在您未提及的选择中,否则无需读取用户组表。
“isSharing 或 userId”应该让您拥有自己的计算机以及任何共享计算机。这可能是不必要的聪明:一个简单的联合可能更有效。
【讨论】:
【参考方案2】:好的,我认为您想要上述规范的表和查询?
我从规范中得知,计算机已“分配”给给定用户,但可以共享?
计算机(int userId)
看看这个,如果您想更改任何规格,请告诉我。
DECLARE @Users TABLE(
UserID INT
)
DECLARE @Computers TABLE(
ComputerID INT,
UserID INT
)
DECLARE @Groups TABLE(
GroupID INT
)
DECLARE @GroupMemberships TABLE(
UserID INT,
GroupID INT,
IsSharing INT
)
INSERT INTO @Users (UserID) SELECT 1
INSERT INTO @Users (UserID) SELECT 2
INSERT INTO @Computers (ComputerID, UserID) SELECT 1, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 2, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 3, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 4, 2
INSERT INTO @Computers (ComputerID, UserID) SELECT 5, 2
INSERT INTO @Groups (GroupID) SELECT 1
INSERT INTO @Groups (GroupID) SELECT 2
INSERT INTO @Groups (GroupID) SELECT 3
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 1, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 2, 1
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 2, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 3, 0
DECLARE @UserID INT
--SELECT @UserID = 1
SELECT @UserID = 2
SELECT DISTINCT
ComputerID
FROM @Computers
WHERE UserID = @UserID
UNION
SELECT DISTINCT
ComputerID
FROM @Computers c INNER JOIN
(
SELECT DISTINCT
gm.UserID
FROM @GroupMemberships gm INNER JOIN
@GroupMemberships ThisUserGroups ON gm.GroupID = ThisUserGroups.GroupID
AND ThisUserGroups.UserID = @UserID
WHERE gm.UserID != @UserID
AND gm.IsSharing = 1
) OtherUsersInSharedGroups ON c.UserID = OtherUsersInSharedGroups.UserID
【讨论】:
啊,谢谢,这看起来像我现在用 ORM 做的……但是有两个子查询,这个查询会有效吗?是否值得将其作为物化视图? 是的,每台计算机只有一个用户,但每个用户可能有多台计算机;谢谢! 子查询不是必需的,您可以修改它,但这是我在阅读您的问题时输入的方式 X-)。如果表上的索引很好,我认为你不会有太多问题。此外,您可能希望使用参数来使用查询或表函数。此外,如果值没有定期更改,为什么不缓存这些值,您甚至可以在选择中添加一个额外的字段,指示计算机是直接的,还是与其他计算机共享的。缓存值可能会使事情变得更快,但请记住在更新、删除和插入时清除缓存以上是关于查询/数据库优化:如何优化? (我应该使用物化视图吗?)的主要内容,如果未能解决你的问题,请参考以下文章
clickhouse,数据查询与写入优化,分布式子查询优化,外部聚合/排序优化,基于JOIN引擎的优化,SQL优化案例,物化视图提速,查询优化常用经验法则,选择和主键不一样的排序键,数据入库优化(代码
ClickHouse的介绍(基本sql操作,以及数据库引擎表引擎分片副本explain优化物化视图等)