子查询与连接性能

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;

上面的查询没有 WHEREHAVING 子句,因此我没有看到任何明显的方法可以使用索引进一步调整它。但它至少比您当前的查询具有潜在优势,即它不涉及 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 上有过滤器,则相关子查询方法通常要快得多,因为只有查询返回的站点用于计算。