子查询与连接性能
Sub query vs joins performance
我有两个表 Sites 和 ScannedItems。 Sites 有大约 15000 行,ScannedItems 有大约 6000 万行。下面的查询需要大约 6 分钟,索引为 CountUniqueRoleAssignments、Modified 和 siteid。这可以以任何方式优化吗?某种连接是否比子查询或任何其他提示更快?
select
*,
(select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.CountUniqueRoleAssignments>0) as CountUniquePermissions,
(select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.Modified<DATEADD (day, -30 ,GETDATE())) as CountNotModified30Days
from sites
我可能会使用联接来编写此查询:
SELECT
s.siteid,
COALESCE(si.CountUniquePermissions, 0) AS CountUniquePermissions,
COALESCE(si.CountNotModified30Days, 0) AS CountNotModified30Days
FROM sites s
LEFT JOIN
(
SELECT siteid,
COUNT(CASE WHEN CountUniqueRoleAssignments > 0 THEN 1 END)
AS CountUniquePermissions,
COUNT(CASE WHEN Modified < DATEADD (day, -30, GETDATE()) THEN 1 END)
AS CountNotModified30Days
FROM ScannedItems
GROUP BY siteid
) si
ON si.siteid = s.siteid
ORDER BY
s.siteid;
上面的查询没有 WHERE
或 HAVING
子句,因此我没有看到任何明显的方法可以使用索引进一步调整它。但它至少比您当前的查询具有潜在优势,即它不涉及 select 子句中相关子查询的 N^2
行为。
您可以使用 LEFT JOIN
和条件聚合,如下所示:
select
S.siteid,
COUNT(CASE WHEN SI.CountUniqueRoleAssignments > 0 THEN 1 END) as CountUniquePermissions,
COUNT(CASE WHEN SI.Modified<DATEADD (day, -30 ,GETDATE()) THEN 1 END ) as CountNotModified30Days
from sites S
LEFT JOIN ScannedItems SI ON SI.siteid = S.siteid
GROUP BY S.SITEID
对于任何性能问题,您应该在您的数据上测试 SQL 代码。对于此查询:
select s.*,
(select COUNT(*)
from ScannedItems si
where si.siteid = s.siteid and si.CountUniqueRoleAssignments > 0
) as CountUniquePermissions,
(select COUNT(*)
from ScannedItems si
where si.siteid = s.siteid and si.Modified < DATEADD(day, -30 ,GETDATE())
) as CountNotModified30Days
from sites s;
我会推荐两个索引:
ScannedItems(siteid, CountUniqueRoleAssignments)
ScannedItems(siteid, Modified)
.
有了这两个索引,我希望它比任何使用聚合的替代方法都要快。为什么?聚合是一项相当昂贵的操作。相关子查询只能使用索引进行处理。
这是在扫描两个索引(与单个聚合相比),因此不能 100% 保证这是否更快——这就是您应该对其进行测试的原因。但是,如果您在 sites
table 上有过滤器,则相关子查询方法通常要快得多,因为只有查询返回的站点用于计算。
我有两个表 Sites 和 ScannedItems。 Sites 有大约 15000 行,ScannedItems 有大约 6000 万行。下面的查询需要大约 6 分钟,索引为 CountUniqueRoleAssignments、Modified 和 siteid。这可以以任何方式优化吗?某种连接是否比子查询或任何其他提示更快?
select
*,
(select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.CountUniqueRoleAssignments>0) as CountUniquePermissions,
(select COUNT(*) from ScannedItems where ScannedItems.siteid=sites.siteid and ScannedItems.Modified<DATEADD (day, -30 ,GETDATE())) as CountNotModified30Days
from sites
我可能会使用联接来编写此查询:
SELECT
s.siteid,
COALESCE(si.CountUniquePermissions, 0) AS CountUniquePermissions,
COALESCE(si.CountNotModified30Days, 0) AS CountNotModified30Days
FROM sites s
LEFT JOIN
(
SELECT siteid,
COUNT(CASE WHEN CountUniqueRoleAssignments > 0 THEN 1 END)
AS CountUniquePermissions,
COUNT(CASE WHEN Modified < DATEADD (day, -30, GETDATE()) THEN 1 END)
AS CountNotModified30Days
FROM ScannedItems
GROUP BY siteid
) si
ON si.siteid = s.siteid
ORDER BY
s.siteid;
上面的查询没有 WHERE
或 HAVING
子句,因此我没有看到任何明显的方法可以使用索引进一步调整它。但它至少比您当前的查询具有潜在优势,即它不涉及 select 子句中相关子查询的 N^2
行为。
您可以使用 LEFT JOIN
和条件聚合,如下所示:
select
S.siteid,
COUNT(CASE WHEN SI.CountUniqueRoleAssignments > 0 THEN 1 END) as CountUniquePermissions,
COUNT(CASE WHEN SI.Modified<DATEADD (day, -30 ,GETDATE()) THEN 1 END ) as CountNotModified30Days
from sites S
LEFT JOIN ScannedItems SI ON SI.siteid = S.siteid
GROUP BY S.SITEID
对于任何性能问题,您应该在您的数据上测试 SQL 代码。对于此查询:
select s.*,
(select COUNT(*)
from ScannedItems si
where si.siteid = s.siteid and si.CountUniqueRoleAssignments > 0
) as CountUniquePermissions,
(select COUNT(*)
from ScannedItems si
where si.siteid = s.siteid and si.Modified < DATEADD(day, -30 ,GETDATE())
) as CountNotModified30Days
from sites s;
我会推荐两个索引:
ScannedItems(siteid, CountUniqueRoleAssignments)
ScannedItems(siteid, Modified)
.
有了这两个索引,我希望它比任何使用聚合的替代方法都要快。为什么?聚合是一项相当昂贵的操作。相关子查询只能使用索引进行处理。
这是在扫描两个索引(与单个聚合相比),因此不能 100% 保证这是否更快——这就是您应该对其进行测试的原因。但是,如果您在 sites
table 上有过滤器,则相关子查询方法通常要快得多,因为只有查询返回的站点用于计算。