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


10

我正在努力寻找有关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

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

任何帮助,我们将不胜感激,

非常感谢,

编辑:

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


5
它不会持久存储在表的数据页面中,但是会持久存储在index的页面中。
亚伦·伯特兰

非持久计算列不会物理存储在表中。它们是虚拟列。每次在查询中引用它们时,都会重新计算它们的值。看到这个参考
Kin Shah

Answers:


11

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

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

亲自查看此内容的最佳方法是实际执行此操作:创建这些对象,并执行涉及InvoiceStatusID字段的更新。如果需要帮助查看索引更新发生的位置,请发布执行计划(PasteThePlan.com对此有所帮助)。


1
@ Uberzen1不,正如他解释的那样,它是在插入/更新时写入索引页的。如果使用索引访问列,则不必重新计算任何内容。
亚伦·伯特兰

啊! 我现在和你在一起,对不起!
Uberzen1

6
@起泡很好,没有冒犯,但我认为那不是布伦特。他们可以将相同的XML粘贴到Dropbox,MSDN论坛上,基本上在任何在线位置上……现在,每个在线服务都需要负责可能被上传文件的人泄露的秘密吗?
亚伦·伯特兰

2
@blobbles是的,您只是无法阻止人们分享。嘿,顺便说一下,在Instagram上关注我-我是BrentO-我在那分享早餐的照片。;-)
布伦特·奥扎尔

4
隐私链接中的@blobbles指出:您在此处复制/粘贴的数据是公开的。任何人都可以阅读。没有安全性。
ypercubeᵀᴹ

8

已索引的,非持久的计算列的值不会持久存储在的数据页面中,而是会持久存储在索引的页面中。不管它是持久存储在0、1还是多个索引中,它都保持在表中非持久性。

为了说明布伦特的描述,以您给出的示例为例,让我们插入一行:

INSERT dbo.Invoice(CustomerID, InvoiceStatus) VALUES(1,N'Sent');

现在,让我们看一下索引页面:

DBCC TRACEON(3604, -1);
DBCC IND(N'dbname', N'dbo.Invoice', 2);

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

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

在此处输入图片说明

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

DBCC PAGE(7, 1, 584, 3);

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

输出:

在此处输入图片说明

在索引页面上。


非常酷,谢谢亚伦。我最初问这个问题的原因是,我在现实世界中部署类似的索引时遇到了一些实际麻烦,并且想确切了解幕后情况,以便我找出问题所在。这很有帮助,谢谢!
Uberzen1

1
@ Uberzen1您可以定义“真正的麻烦”吗?您要发布有关问题的问题吗?
亚伦·伯特兰

我可能会做,我本人将首先对其进行更深入的研究,但只是想了解create index语句究竟在做什么。TLDR是;我有一个大表,类似于上面的发票表,它有大约400m记录,并且不幸的是,OrderStatus列恰好位于表的中间,使索引等工作有些痛苦。现在,我们添加了一个计算列,以最终将其持久保存并将varchar字段移至其自己的表中。1/2
Uberzen1

5
@ Uberzen1是的,因为在写入索引时,计算列实际上已在磁盘上实现,因此必须记录所有该活动。一种解决方法可能是停止依赖计算的列-将表达式放入视图或临时查询中,如果这不是一种选择,则可以创建一个新的可为空的列,将其分块更新(以避免日志被杀死) ,然后删除计算列,重命名新列,然后更改DML以手动编写。但是实际上,由于您可以从现有数据中获取冗余信息,因此我会选择第一个选项。
亚伦·伯特兰

2
非常感谢亚伦。我很高兴您提到在它前面放一个视图,因为那也是我要解决的问题,也许是时候重新考虑这个想法了!
Uberzen1

7

PERSISTED计算列的属性与值是否持久化在(聚集索引或堆)中有关,而与值是否持久化在索引中无关。

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

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

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

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

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

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

...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.