在非聚集索引中添加所有主键约束

问题描述

我用以下几列创建了一个表。所有列都是唯一键(列),我的表中没有主键。

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,[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。

在您的情况下,由于您的主键是非集群,因此用相同的字段创建另一个索引是没有意义的。要读取数据,它必须从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的视频-我强烈推荐它,因为它对我如何使用索引很有帮助。


关于您的特定示例-在索引中有两件事需要寻找:

  1. “探寻”数据集中特定点的能力(基于索引的种类)。
  2. 减少阅读量的能力。

就允许搜索而言,您需要以最适当的方式对索引进行排序。在进行过滤时(例如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)

差异

  • 它是群集的,而不是非群集的
  • 通过pgm_name重新排列了第3个字段和第4个字段的顺序
  • 由于没有太多其他内容需要阅读,因此不需要第二个非聚集索引。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...