【问题标题】:SQL Server 2016 : query performance with join and without joinSQL Server 2016:有联接和无联接的查询性能
【发布时间】:2021-03-23 14:06:51
【问题描述】:

我有 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。

现在我想要 col1 和 col2 值的 category=1(来自 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 是否可以节省数百万行 TABLE1 的表扫描时间。

限制是: 在我的真实数据库场景中,我可以将 Table1.Id 设为非聚集索引,将 Table1.category 设为非聚集索引。我无法将 Table1.Id 作为聚集索引,因为在实际场景中我的 Table1 中实际上有另一个自动增量列作为主键。因此,请与此限制分享您的想法。

请确认并分享对此的想法。

【问题讨论】:

  • 你用的是什么数据库?您标记的 sql-serverplsql 相互矛盾
  • 对不起,我正在使用 Ms SqlServer 2016
  • 让单个查询决定您的架构设计有点不寻常。如果有两个表是有意义的,那么就采用这种设计,而是专注于使用索引之类的东西使第二个查询更快。
  • 将相同的数据存储在两个不同的表中(几乎)从来都不是一个好主意。
  • "以下 2 个查询中哪个查询的输出速度更快?"这可以在您的系统上进行测试。如果我们没有数据,谁能知道两者中哪一个更快?

标签: sql sql-server join inner-join


【解决方案1】:

这取决于现有的索引。对于 T1 中的 Id 上的非聚集索引,解决方案 2 可能比解决方案 1 执行得更好,这将需要完整的表扫描来选择具有 category1 的行。相反,如果我们在 Category 上也有一个非聚集索引,那么解决方案 1 会更快,因为它只需要寻找非聚集索引来查找行。

如果 T1 上的 Id 没有任何索引,则需要完整扫描才能找到 T2.Id 行,因此解决方案 2 可能需要对 T1 进行 10 次完整扫描,而针对解决方案 1 可能需要对 T1.Category 进行 1 次完整扫描,因此解决方案 1 可能更快。但这取决于查询优化器和测试真实案例,看看实际执行计划是什么是最好的回答方式。

但要走的路是实现正确的模型,然后继续创建使查询快速运行所需的索引。

编辑:根据查询编辑调整答案。 Edit2:索引覆盖会很昂贵,并且在表 1 上的 PK 上查找 10 个索引不会花费太多。

【讨论】:

  • 但在我的真实数据库场景中,我可以将 Table1.Id 设为非聚集索引,而 Table1.category 也可以设为非聚集索引。我无法将 Table1.Id 作为聚集索引,因为在实际场景中我的 Table1 中实际上有另一个自动增量列作为主键。因此,请与此限制分享您的想法。
  • 为了清楚起见,我现在已经包含了 TABLE1 的主键。还纠正了我的内部连接条件。
  • 请立即检查 2 个查询
  • @Mahen,主键索引可以是聚集的或非聚集的。 PK 索引默认是聚集的,除非表已经有聚集索引。考虑工作负载中的所有查询以选择最合适的聚集索引。需要权衡取舍。
  • @DanGuzman 当然。由于我的主键是标识列,因此它将成为聚集的。因此,我必须将 Table1.category 设为非聚集索引,并将 Table1.Id 设为非聚集索引。正如 sergiom 所说,Solution1 对我来说更可取。
【解决方案2】:

[通知]

这个答案是针对旧版本的问题,https://stackoverflow.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 不会避免表扫描。更糟糕的是:

解决方案 1 全表扫描

解决方案 2 对 T2(T2.category)进行全表扫描,然后进行嵌套循环(T2.category = T1.category)

请问,你在这里的目标是什么?

【讨论】:

  • 抱歉,解决方案2,其中条件:INNER JOIN TABLE1 T1 ON T1.Id = T2.Id
  • 请立即检查 2 个查询
【解决方案3】:

首先,这句话表明缺乏对数据库的理解:

类别 = 1 的前 10 行

SQL 表代表无序 集合。没有“前 10 行”之类的东西。在您的问题的上下文中,我认为您的意思是“id 值最低的 10 行”。但是,从引擎的角度来看,表的排序仍然是任意的。在某些情况下,可以合理地将 聚集 索引假定为“表排序”,但不能保证:

select *
from t;

以特定顺序返回数据即使使用聚集索引

第一个查询的两个可能的执行计划——取决于索引——是:

  1. 扫描表格(即读取数百万行)并对每一行进行测试。
  2. 扫描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 Server 中的聚集索引的说明。如果ididentity主键且没有其他聚集索引,则自动作为聚集索引使用。

【讨论】:

  • 是的,通过说前 10 行,我只想将前 10 行从 Table2 插入到 Table1 中,category=1 并自动递增主键 masterId
  • @如果你在 SQL Server 中有一个identity 主键,那么它就是聚集索引。即使在category 上有索引,这可能会使第二种方法略快于第一种方法,但两者的差异都会非常小。
  • 好的。我肯定会更喜欢身份 PrimaryKey 而不是自己为每个插入创建唯一的主键。在 Category 列上具有非聚集索引之后,我理解如您所说,Solution2 会稍微快一些,但差异会非常小。由于差异非常小,我将保留 Solution1,它已经与其他表进行了大约 6 个连接。仅依靠类别上的非聚集索引将避免对具有大量数据的 Table1 进行全表扫描。希望我的想法是有效的。
  • 我已经在 MS SQL Server 2016 中验证了这一点。如果我只尝试用 SSMS 来做,你所说的就很好。但是通过手动编写创建表查询,我可以在身份列上创建具有非聚集索引的主键,该索引会自动递增。而且我能够创建另一列作为聚集索引。所以这是 SSMS 中的一个错误。
  • 所以有了这个理解,我会更喜欢将 Table1.masterId 作为标识列和具有非聚集索引的主键。并将 Table1.category 设为聚集索引。希望与非集群类别相比,这将提供更好的性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
相关资源
最近更新 更多