sql-server - 在非聚集索引中添加所有主键约束
问题描述
我创建了一个包含以下列的表。所有列都是唯一键(列)我的表中没有主键。
表Product
:
Bat_Key,
product_no,
value,
pgm_name,
status,
industry,
created_by,
created_date
我改变了我的表来添加约束
ALTER TABLE [dbo].[Product]
ADD CONSTRAINT [PRODUCT_PK]
PRIMARY KEY NONCLUSTERED ([Bat_Key] ASC, [product_no] ASC,
[value] ASC, [pgm_name] ASC, )
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
GO
如果我创建如下索引:
CREATE NONCLUSTERED INDEX [PRODUCT_BKEY_PNO_IDX]
ON [dbo].[PRODUCT] ([Bat_Key] ASC, [product_no] ASC, [value], [pgm_name])
INCLUDE ([status], [industry])
WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
此设计是否适用于以下选择查询:
select *
from Product
where Bat_Key = ? and product_no=?
order by product_no, pgm_name;
select *
from Product
where Bat_Key = ? and product_no=? and pgm_name = ? and value = ?
select *
from Product
where Bat_Key = ? and product_no=?
delete from Product
where Bat_Key = ? and product_no=?
还是应该根据我的where
条款创建不同的索引?
解决方案
聚集索引与非聚集索引非常不同。实际上,两个索引的类型都包含根据您指定的列排序的数据。然而,
- 聚集索引还包含表中的其余数据(除了 nvarchar(max) 之类的一些数据)。您可以认为这是它在数据库中的保存方式
- 非聚集索引仅包含您已包含在索引中的列
如果您没有聚集索引,则您有一个“堆”。它们不是 PK,而是内置的行标识符。
在您的情况下,由于您的主键是non-clustered,因此创建另一个具有相同 fields 的索引是没有意义的。要读取数据,它必须从您的 PK 中获取行标识符,然后从堆中读取数据。
另一方面,如果您的主键是聚集的(这是默认设置),那么在某些情况下,在字段上使用非聚集索引可能会很有用。但请注意,您添加的每个非聚集索引也会减慢更新、插入和删除的速度(因为也必须维护索引)。
在您的示例中 - 假设您有一个字段,该字段是包含大量信息的行上的 varchar(8000)。甚至要从聚集索引中读取一行,它必须从其他字段读取(例如)100 个字节,并且从该新字段读取最多 8000 个字节。换句话说,它将您需要阅读的数量乘以 80 倍。
我倾向于看到具有两种类型数据的表
- 您汇总的数据
- 您只关心逐行级别的数据
例如,在交易表中,您可能有 transaction_id、transaction_date、transaction_amount、transaction_description、transaction_entered_by_user_id。
- 在大多数情况下,每当您获得总计等时,您会经常需要交易金额、查看总计时的日期(例如,本周的交易总数是多少?)
- 另一方面,description 和 user_id 仅在您引用特定行时使用(例如,谁进行了此特定事务?)
- 在这些情况下,我经常在聚合中使用的字段上放置一个非聚集索引,即使它们与聚集索引重叠。它只是减少了所需的读取量。
关于这方面的一个非常好的视频是由 Brent Ozar 拍摄的,名为How to think like the SQL Server Engine - 我强烈推荐它,因为它对我理解如何使用索引有很大帮助。
关于您的具体示例 - 在索引中有两件事要查找:
- “寻找”到数据集中特定点的能力(基于索引的排序)。
- 减少阅读量的能力。
在允许搜索方面,您需要以最合适的方式对索引进行排序。在进行过滤时(例如,WHERE 子句、JOIN,一个经验法则是首先查找“精确”匹配项。对于这些,它们的顺序无关紧要,只要它们全部匹配即可到那时。
在你的情况下,你有
where Bat_Key = ? and product_no=?
where Bat_Key = ? and product_no=? and pgm_name = ? and value = ?
这表明您的前两个字段应该是 Bat_Key 和 product_no(按任意顺序)。然后你也可以拥有 pgm_name 和 value(也可以按任意顺序)。
你也有
where Bat_Key = ? and product_no=?
order by product_no, pgm_name;
这向我建议第三个字段应该是 pgm_name(作为 Bat_Key、product_no 和 pgm_name 的索引将提供您需要的内容)。
但是 - 这是一个很大的问题 - 你有很多 * 在那里,例如,
select *
from Product
where Bat_Key = ? and product_no=?
因为您选择 *,所以任何不是聚集索引的索引也需要返回到实际行以获取包含在 * 中的其余内容。
由于这些需要表中的所有字段(不仅仅是索引中的字段),因此需要返回堆(在您的情况下)。如果您在上面的字段上有一个聚集索引以及一个非聚集索引,那么无论如何它都必须从聚集索引中读取,因为那里有您的查询所需的信息。
再一次 - 上面的视频 - 比我更好地解释了这一点。
因此,在您的情况下,我建议使用以下主键
ADD CONSTRAINT [PRODUCT_PK]
PRIMARY KEY CLUSTERED ([Bat_Key] ASC, [product_no] ASC,
[pgm_name] ASC, [value] ASC)
差异
- 它是集群的而不是非集群的
- 重新排列第 3 和第 4 字段的顺序以帮助按 pgm_name 排序
- 没有真正需要第二个非聚集索引,因为没有太多其他要阅读的内容。
推荐阅读
- reactjs - 自定义列表布局
- hibernate - 加入具有单独 id 的表
- azure-data-factory - ADF V2 - 从 blob (csv) 的文件路径设置参数值
- python - 避免在 python 脚本名称中出现关于破折号的 pylint 警告
- c# - ASP.NET Core 身份验证,基于控制器的响应类型
- java - 如何转换可选
- > 到可选
- >
- django - Django 使用转换变量进行注释
- python - Keras 中的嵌入功能不适用于 float 16
- android - 无法从仪器测试中的清单中获取应用程序名称
- python - Google Colab:重新设置 Python 模块