【问题标题】:Original insertion order with Clustered Index带有聚集索引的原始插入订单
【发布时间】:2012-07-01 19:15:06
【问题描述】:

我有一个关于聚集索引的问题。

在聚集索引中,叶级节点本身按排序顺序保存数据,对吗?

也就是说,每次插入/更新/删除时,节点都会重新洗牌以保持排序顺序。

那么如何按照插入的顺序从中检索数据呢?

假设以下数据按给定顺序插入:1,7,4,5,2,并在该字段上创建聚集索引。

所以数据将按1,2,4,5,7 的顺序在内部存储,对吗?

所以这可能有助于更快地查找特定值,但是如果用户希望按照他插入的顺序排列前 3 个值怎么办?

它们是否可以以某种方式检索,或者我是否必须为插入的每一行分配一个增量 id,在其上声明一个非聚集索引,并根据该 id 字段上的记录排序提供前 3 条记录的数据?

【问题讨论】:

  • 是的,您需要添加一个额外的列来表示增量代理,例如identity INT,或者某种形式的自动时间戳机制来重新跟踪原始插入顺序。
  • 那么如何选择是对字段值创建聚集索引,还是对id值创建聚集索引呢?是这样的,当更多的查询是这种类型时选择从第3行开始的6行我应该选择id作为聚集键,当查询像选择value = 45的记录比较频繁,我应该在字段值上设置聚集索引吗?
  • 但我刚刚了解到,我不能在一个表中创建超过 1 个聚集索引,也不能在同一个表中同时创建聚集索引和非聚集索引。那么如何在此处的 2 个不同字段上创建 2 个索引(其中一个至少应聚集以促进快速查找)?
  • 除了零个或一个聚集索引之外,您还可以在一个表上创建许多非聚集索引。

标签: sql indexing clustered-index


【解决方案1】:

(基于 SQL Server 的答案 - 问题未 100% 指定)

在聚集索引中,叶级节点本身按排序顺序保存数据,对吗?

这不太正确,数据可以按任何顺序存储在叶子上,但页面上的插槽数组实际上是从页面外读取数据的顺序 - 而不是数据的物理顺序。

也就是说,每次插入/更新/删除时,节点都会重新洗牌以保持排序顺序。

节点(例如页面被拆分并且双链表上的前向/后向指针改变),但在页面内,槽数组仍然是保留顺序的实体,行本身不会被打乱以匹配槽数组顺序。

那么如何按照插入的顺序从中检索数据呢?

通常不保证它会按照确切的顺序 - 这往往发生在堆页上,其中槽数组更能代表顺序,但同样不能保证。

假设以下数据按给定顺序插入:1、7、4、5、2,并在该字段上创建聚集索引。所以数据将按 1、2、4、5、7 的顺序在内部存储,对吗?

不,它将在页面上存储 1,7,4,5,2,但插槽数组会将页面上的地址读取为 7,5,4,2,1(它从页面向后,因此您可以反向阅读。)

所以这可能有助于更快地查找特定值,但是如果用户希望按照他插入的顺序排列前 3 个值怎么办?

在这种情况下有点无关紧要——除了没有关于排序的保证之外,SQL 会将整个页面读入内存。如果你想了解更多关于这种级别的 SQL 内部知识,我仍然会推荐 Kalen Delaneys SQL Internals 这本书作为最好的来源之一。

如果您想了解有关插入顺序的任何信息,我建议您使用某种 insert_timestamp

【讨论】:

  • 这不太正确,数据可以按任何顺序存储在叶子上,但是页面上的插槽数组实际上是从页面读取数据的顺序 -不是数据的物理顺序。 - 但我在 mssqltips.com/sqlservertip/1254/clustered-tables-vs-heap-tablesClustered Table 段落下发现 Data 基于聚集索引键按顺序存储。请解释哪一个是正确的,或者它们实际上是相同的陈述。
  • 它们是不同深度的技术细节的解释。您可以将其视为按顺序存储的高级别的 - 不关注 SQL Server 物理上是如何做到这一点的 - 在更详细的级别上,您将了解页面、槽数组以及聚集索引如何维护顺序等。
  • 请解释以一种顺序将数据存储在数组槽中,同时以另一种顺序读取的含义。我正在查看的所有书籍都指出,在聚集索引中,叶节点是存储数据的地方(与非聚集索引相反,它们只是指向实际数据页的指针),并且它们存储在排序顺序,服务器维护每次更新/插入/删除的排序顺序。
  • 它们存储在页面的双链表中,按顺序遍历该列表,您就有了聚集索引的顺序。这并没有说明页面如何存储在磁盘上,或者单个页面上的数据是否有序,插槽数组是单个页面中的顺序。在一个问题上,完整的解释比 cmets 需要更多的空间/时间
  • 在我的回答中提到 - Kalen Delaney,SQL Server Internals - 被许多人认为是内部圣经。一些博客也涵盖了这一级别的详细信息,请阅读 sqlskills 博客,例如 Paul Randal 的:sqlskills.com/blogs/paul
【解决方案2】:

对我来说,听起来你想要在你的行上加上一个时间戳。我通常会在我创建的所有表上放置以下列(用于审计):

timecreated
timemodified
createdby
modifiedby
deleted

这些列让您知道谁创建了该行,何时、何时、最后一次修改以及由谁以及可选地“软删除”该行,方法是将已删除设置为 true。当然,系统中的所有其他查询都必须检查已删除的布尔值才能使软删除起作用。

【讨论】:

    【解决方案3】:

    表数据按照聚集索引的顺序排序。 每个表只能有一个聚集索引,如果你想按照他插入的顺序检查前 3 个值,

    使用 AdventureWorks

    创建表 myTable99(
    Col1 int IDENTITY(1,1) PRIMARY KEY , Col2 Char(1) , Col3 日期时间 DEFAULT getdate()

    ) 去

    插入 myTable99(Col2) SELECT 'A' UNION ALL SELECT 'B' UNION ALL 选择'C'去

    从 myTable99 中选择 * 3 人订购 去吧

    删除表 myTable99 去吧

    其他方法可以是:

    CREATE TABLE CounterData]( [CounterDataID] [bigint] IDENTITY(1,1) NOT NULL, [DateTimeID] [bigint] NOT NULL, [Value] [float] NULL ) ON [主要]

    创建唯一聚集索引 [IX_DateTime_CounterDataID] 开启 [PK].[CounterData]

    (

    [日期时间ID] ASC,
    [CounterDataID] ASC

    )

    (PAD_INDEX = 关闭,STATISTICS_NORECOMPUTE = 关闭,SORT_IN_TEMPDB = 关闭,IGNORE_DUP_KEY = 关闭,DROP_EXISTING = 关闭,在线 = 关闭, ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO

    【讨论】:

    • 所以基本上你的意思是存储一个额外的数据(在这种情况下,一个时间戳)作为代理,并在此基础上按顺序检索记录,同时随机基于聚集索引服务器本身,对吧?
    猜你喜欢
    • 2012-07-21
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 2011-02-24
    • 2015-07-04
    • 2010-09-10
    相关资源
    最近更新 更多