在非持久化计算列 SQL Server 上创建非聚集索引

Ube*_*en1 13 index sql-server sql-server-2012 computed-column

我正在努力寻找有关 SQL Server 如何实际存储非持久计算列的任何文档。

以下面的例子为例:

--SCHEMA
CREATE TABLE dbo.Invoice
(
    InvoiceID INT IDENTITY(1, 1) PRIMARY KEY,
    CustomerID INT FOREIGN KEY REFERENCES dbo.Customer(CustomerID),
    InvoiceStatus NVARCHAR(50) NOT NULL,
    InvoiceStatusID AS CASE InvoiceStatus 
                         WHEN 'Sent' THEN 1 
                         WHEN 'Complete' THEN 2
                         WHEN 'Received' THEN 3
                       END
)
GO

--INDEX
CREATE NONCLUSTERED INDEX IX_Invoice ON Invoice
(
    CustomerID ASC
)
INCLUDE
(
    InvoiceStatusID
)
GO
Run Code Online (Sandbox Code Playgroud)

我知道它存储在叶级别,但是如果该值没有持久化,那么如何存储任何内容?在这种情况下,索引如何帮助 SQL Server 找到这些行?

非常感谢任何帮助,

非常感谢,

编辑:

感谢 Brent & Aaron 回答这个问题,这里的 PasteThePlan清楚地展示了他们的解释。

Bre*_*zar 11

当 SQL Server 在计算域上创建索引时,计算域会在那时写入磁盘 - 但仅限于该索引的 8K 页。SQL Server 可以在读取聚集索引时计算 InvoiceStatusID - 无需将该数据写入聚集索引。

当您在 dbo.Invoice 中删除/更新/插入行时,索引中的数据会保持最新。(当 InvoiceStatus 更改时,SQL Server 知道也更新 IX_Invoice。)

您可以亲眼看到这一点的最佳方式是实际操作:创建这些对象,并执行触及 InvoiceStatusID 字段的更新。如果您需要帮助查看索引更新发生的位置,请发布执行计划(PasteThePlan.com 对此很有帮助)。

  • @blobbles 好吧,没有冒犯,但我认为这不是布伦特。他们可以将相同的 XML 粘贴到 Dropbox、MSDN 论坛、这里,基本上是在线的任何地方……现在是否每个在线服务都需要对上传文件的人可能泄露的机密负责? (6认同)
  • @blobbles 在隐私链接中声明:***您在此处复制/粘贴的数据是公开的**。任何人都可以阅读它。没有安全保障。* (4认同)
  • @blobbles 是的,你就是无法阻止人们过度分享。嘿,顺便说一句,在 Instagram 上关注我 - 我是 BrentO - 我在那里分享我的早餐照片。;-) (2认同)

Aar*_*and 8

索引的、非持久化计算列的值不持久化在的数据页中,但持久化在索引页中。它在表中保持非持久化,无论它是在 0、1 还是多个索引中持久化。

只是为了说明 Brent 的描述,以您给出的示例为例,让我们插入一行:

INSERT dbo.Invoice(CustomerID, InvoiceStatus) VALUES(1,N'Sent');
Run Code Online (Sandbox Code Playgroud)

现在,让我们看看索引页:

DBCC TRACEON(3604, -1);
DBCC IND(N'dbname', N'dbo.Invoice', 2);
Run Code Online (Sandbox Code Playgroud)

(显然更改dbname,并且在您的情况下索引 ID 可能不是 2。)

输出(你的肯定会有所不同):

在此处输入图片说明

最后,让我们检查页面PageType2:

DBCC PAGE(7, 1, 584, 3);
Run Code Online (Sandbox Code Playgroud)

(您可能需要更改 7 以匹配您的数据库 ID,如果您有多个数据文件,您可能需要更改第二个参数以匹配PageFID第一个结果。)

输出:

在此处输入图片说明

那是在索引页上。

  • @Uberzen1 是的,因为在写入索引时计算列实际上已具体化到磁盘上,所以必须记录所有这些活动。一种解决方法可能是停止依赖计算列 - 将该表达式放入视图或临时查询中,如果这不是一个选项,您可以创建一个新的可为空的列,分块更新它(以避免日志杀死) ,然后删除计算列,重命名新列,并将您的 DML 更改为手动编写。但实际上,由于它是您可以从现有数据中获得的冗余信息,因此我会选择第一个选项。 (5认同)
  • 非常感谢亚伦。我很高兴你提到在它前面放置一个视图,因为这也是我的解决方案,也许是时候重新考虑这个想法了! (2认同)

ype*_*eᵀᴹ 8

PERSISTED计算列的属性与值是否保留在表中(聚集索引或堆)有关,而不是值是否保留在索引中。

CREATE INDEX有关于计算列和索引的限制要求:

可以包括确定性且精确或不精确的计算列。从 image、ntext、text、varchar(max)、nvarchar(max)、varbinary(max) 和 xml 数据类型派生的计算列可以包含在非键列中,只要计算列数据类型允许作为包含柱子。有关更多信息,请参阅计算列上的索引。

计算列是否持久化没有限制。

以及进一步(不是关于包含而是关于索引主要部分中的计算列):

可以在计算列上创建索引。此外,计算列可以具有属性PERSISTED。这意味着数据库引擎将计算值存储在表中,并在计算列所依赖的任何其他列更新时更新它们。数据库引擎在为列创建索引以及在查询中引用索引时使用这些持久值。

要索引计算列,计算列必须(是)确定性和精确的。但是,使用该PERSISTED属性将可索引计算列的类型扩展为包括:

...