SQL Server 2016:使用连接和不使用连接的查询性能
SQL Server 2016 : query performance with join and without join
我有 2 个表 TABLE1 和 TABLE2。
- TABLE1 包含列 masterId、Id、col1、col2、category
- TABLE2 有列 Id、col1、col2
TABLE2.Id
是主键,TABLE1.Id
是外键。
TABLE1.masterId 是 TABLE1 的主键。
TABLE1 有 1000 万行,ID 为 1 到 1000 万,前 10 行的类别为 1
TABLE2 只有 10 行 ID 为 1 到 10。
现在我想要类别为 1 的 col1 和 col2 值(来自 TABLE1 或 TABLE2,因为两个表中的值相同)
以下 2 个查询中哪个查询的输出速度更快?
解决方案 1:
SELECT T1.col1, T1.col2
FROM TABLE1 T1
WHERE T1.category = 1
解决方案2:
SELECT T2.col1, T2.col2
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T1.Id = T2.Id
WHERE T1.category = 1
解决方案 2 是否节省 Table 对 TABLE1 的数百万行进行扫描的时间。
限制为:
在我的真实数据库场景中,我可以将 Table1.Id 设为非聚集索引,将 Table1.category 设为非聚集索引。我不能将 Table1.Id 作为聚簇索引,因为在实际情况下,我的 Table1 中实际上有另一个自动递增列作为主键。因此,请就此限制分享您的想法。
请确认并分享对此的想法。
这取决于现有索引。在 T1 中的 Id 上使用非聚集索引,然后解决方案 2 可能比解决方案 1 执行得更好,这将需要完整 table 扫描到 select 类别 1 的行。相反,如果我们在类别上也有一个非聚集索引,那么解决方案 1 会更快,因为它只需要寻找非聚集索引来查找行。
如果 T1 上的 Id 没有任何索引,则需要进行全扫描才能找到 T2.Id 行,因此对于解决方案 2 可能需要对 T1 进行 10 次全扫描,在 T1.Category 上可能需要进行 1 次全扫描对于解决方案 1,因此解决方案 1 可能更快。但这取决于查询优化器和测试真实案例以查看实际执行计划是什么是最好的回答方法。
但要走的路是实施正确的模型,然后继续创建使查询 运行 快速所需的索引。
编辑:根据查询编辑修改答案。
Edit2:索引覆盖会很昂贵,在 table 1 上对 PK 进行 10 次索引搜索不会花费那么多。
[通知]
这个答案是针对旧版本的问题,https://whosebug.com/revisions/65263530/7
当时的情景是:
- T2 也有一个
category
列,并且,
- 第二个查询是:
SELECT T2.col1, T2.col2
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T1.categoryId = T2.category Id
WHERE T2.category = 1
假设唯一的索引是 PK,不,解决方案 2 不会避免 table 扫描。更糟:
解决方案 1
完整 table 扫描
方案二
在 T2 (T2.category) 上进行完整 table 扫描,然后进行嵌套循环 (T2.category = T1.category)
请问,您的目标是什么?
首先,这种说法表明对数据库缺乏了解:
first 10 rows having category = 1
SQL table代表无序集合。没有“前 10 行”这样的东西。在您的问题的上下文中,我认为您的意思是“id
值最低的 10 行”。但是,从引擎的角度来看,table 的顺序 仍然是任意的。在某些情况下, 集群 索引可以合理地假定为“table 排序”,但永远不能保证:
select *
from t;
returns 特定顺序的数据 即使使用聚簇索引。
第一个查询的两个可能的执行计划——取决于索引——是:
- 扫描 table(即读取数百万行)并对每一行进行测试。
- 正在扫描
category
上的索引并仅获取所需的行。
一般来说,当扫描的行数以百万计而返回的行只有几行时,(1) 会比 (2) 慢得多。但是,如果返回了所有记录的很大一部分,这可能不是真的。
我将你的问题解释为询问第二个查询是否比第一个查询更快:
SELECT T2.col1, T2.col2
FROM TABLE2 T2 INNER JOIN
TABLE1 T1
ON T1.Id = T2.Id
WHERE T1.category = 1;
答案是“肯定比扫描快”。如果您在 Table1(id, category)
上有索引,这是可能的。但是,查询最好使用 EXISTS
:
select t2.*
from table2 t2
where exists (select 1
from table1 t1
where t1.id = t2.id and t2.category = 1
);
我希望这也比第一个查询的索引版本更快。即使在 (category)
上有索引,数据库仍然需要为 select
获取数据。如果数据在一页上(如“第一”语句可能暗示的那样),那么两者可能相当可比。但是,很难通过 table1
.
上的正确索引来衡量性能差异
关于 SQL 服务器中聚簇索引的注释。如果 id
是一个 identity
主键并且没有其他聚集索引,那么它会自动用作聚集索引。
我有 2 个表 TABLE1 和 TABLE2。
- TABLE1 包含列 masterId、Id、col1、col2、category
- TABLE2 有列 Id、col1、col2
TABLE2.Id
是主键,TABLE1.Id
是外键。
TABLE1.masterId 是 TABLE1 的主键。
TABLE1 有 1000 万行,ID 为 1 到 1000 万,前 10 行的类别为 1
TABLE2 只有 10 行 ID 为 1 到 10。
现在我想要类别为 1 的 col1 和 col2 值(来自 TABLE1 或 TABLE2,因为两个表中的值相同)
以下 2 个查询中哪个查询的输出速度更快?
解决方案 1:
SELECT T1.col1, T1.col2
FROM TABLE1 T1
WHERE T1.category = 1
解决方案2:
SELECT T2.col1, T2.col2
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T1.Id = T2.Id
WHERE T1.category = 1
解决方案 2 是否节省 Table 对 TABLE1 的数百万行进行扫描的时间。
限制为: 在我的真实数据库场景中,我可以将 Table1.Id 设为非聚集索引,将 Table1.category 设为非聚集索引。我不能将 Table1.Id 作为聚簇索引,因为在实际情况下,我的 Table1 中实际上有另一个自动递增列作为主键。因此,请就此限制分享您的想法。
请确认并分享对此的想法。
这取决于现有索引。在 T1 中的 Id 上使用非聚集索引,然后解决方案 2 可能比解决方案 1 执行得更好,这将需要完整 table 扫描到 select 类别 1 的行。相反,如果我们在类别上也有一个非聚集索引,那么解决方案 1 会更快,因为它只需要寻找非聚集索引来查找行。
如果 T1 上的 Id 没有任何索引,则需要进行全扫描才能找到 T2.Id 行,因此对于解决方案 2 可能需要对 T1 进行 10 次全扫描,在 T1.Category 上可能需要进行 1 次全扫描对于解决方案 1,因此解决方案 1 可能更快。但这取决于查询优化器和测试真实案例以查看实际执行计划是什么是最好的回答方法。
但要走的路是实施正确的模型,然后继续创建使查询 运行 快速所需的索引。
编辑:根据查询编辑修改答案。 Edit2:索引覆盖会很昂贵,在 table 1 上对 PK 进行 10 次索引搜索不会花费那么多。
[通知]
这个答案是针对旧版本的问题,https://whosebug.com/revisions/65263530/7
当时的情景是:
- T2 也有一个
category
列,并且, - 第二个查询是:
SELECT T2.col1, T2.col2
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T1.categoryId = T2.category Id
WHERE T2.category = 1
假设唯一的索引是 PK,不,解决方案 2 不会避免 table 扫描。更糟:
解决方案 1 完整 table 扫描
方案二 在 T2 (T2.category) 上进行完整 table 扫描,然后进行嵌套循环 (T2.category = T1.category)
请问,您的目标是什么?
首先,这种说法表明对数据库缺乏了解:
first 10 rows having category = 1
SQL table代表无序集合。没有“前 10 行”这样的东西。在您的问题的上下文中,我认为您的意思是“id
值最低的 10 行”。但是,从引擎的角度来看,table 的顺序 仍然是任意的。在某些情况下, 集群 索引可以合理地假定为“table 排序”,但永远不能保证:
select *
from t;
returns 特定顺序的数据 即使使用聚簇索引。
第一个查询的两个可能的执行计划——取决于索引——是:
- 扫描 table(即读取数百万行)并对每一行进行测试。
- 正在扫描
category
上的索引并仅获取所需的行。
一般来说,当扫描的行数以百万计而返回的行只有几行时,(1) 会比 (2) 慢得多。但是,如果返回了所有记录的很大一部分,这可能不是真的。
我将你的问题解释为询问第二个查询是否比第一个查询更快:
SELECT T2.col1, T2.col2
FROM TABLE2 T2 INNER JOIN
TABLE1 T1
ON T1.Id = T2.Id
WHERE T1.category = 1;
答案是“肯定比扫描快”。如果您在 Table1(id, category)
上有索引,这是可能的。但是,查询最好使用 EXISTS
:
select t2.*
from table2 t2
where exists (select 1
from table1 t1
where t1.id = t2.id and t2.category = 1
);
我希望这也比第一个查询的索引版本更快。即使在 (category)
上有索引,数据库仍然需要为 select
获取数据。如果数据在一页上(如“第一”语句可能暗示的那样),那么两者可能相当可比。但是,很难通过 table1
.
关于 SQL 服务器中聚簇索引的注释。如果 id
是一个 identity
主键并且没有其他聚集索引,那么它会自动用作聚集索引。