【问题标题】:If clustered index is table data, how can it not be unique?如果聚集索引是表数据,怎么可能不唯一呢?
【发布时间】:2017-01-01 04:33:47
【问题描述】:

我正在寻找如何将 table 从一个 filegroup 移动到另一个,我对为什么我发现的大多数回复都处理 clustered indexes 有一些疑问,因为我的问题与表格。

然后我看了How I can move table to another filegroup?,它说聚集索引是表数据,这解释了使用CREATE CLUSTERED INDEX重新创建聚集索引的原因。

但在同一个问题中,它说如果我的聚集索引是唯一的,则执行其他操作。

我的问题:我假设当我在数据库上创建表时,会为该表创建一个聚集索引。那怎么能不独一无二呢?

谢谢。

【问题讨论】:

    标签: sql-server sql-server-2008


    【解决方案1】:

    如果您有一个 int 数组,并且在其中存储了两次数字 1 - 该数组怎么可能不是唯一的?! (让您思考的技巧问题。它显然不是唯一的。)唯一性是数据的约束。从根本上说,没有什么能阻止您创建在所有列中具有相同值的多行。

    在堆中,这根本不是物理问题。内部行标识符是它在磁盘上的位置。

    在基于 b 树的索引(“聚集索引”)中,物理数据结构确实需要唯一性。请注意,逻辑结构(表)没有。这是一个生理问题。这是一个实现细节。 SQL Server 通过在内部附加一个包含向上计数的序列号的键列来做到这一点。这消除了记录的歧义。您可以通过使用相同的非唯一键创建超过 2^32 行来观察此效果。您将收到错误消息。

    因此,表中有一个您无法访问的隐藏列。它被正式称为“唯一性”。在内部,它用于完成 CI 密钥以使其唯一。它在通常使用唯一 CI 密钥的任何地方存储和使用:在 CI、非唯一 NCI、锁哈希和查询计划中。

    【讨论】:

    • 为了详细说明唯一性,您不需要同时在表中使用带有重复键的所有 2^32 行。我们被这个咬住了,因为我们有一个应用程序插入一个随机数作为 ID(集群键),然后稍后更新它。在我们溢出四字节唯一符后,我们得到了 SQL 666 错误。
    • @BenThul 啊,所以它显然是根据现有的最大唯一性增加的。讨厌。
    • 这是低估了它。 AFAIK,没有办法公开“当前”值(缺少一些DBCC PAGE voodoo),你不能依赖于对当前具有该值的行进行计数。因此,对此进行监控是一场噩梦。例如,您确实必须注意诸如插入率之类的事情以及这对密钥空间耗尽的含义。
    • 但它只适用于非唯一的 CI,对吧?出于某种原因,我从来没有这样的事情。你的用例是什么?您是否需要/想要在一些非密钥上进行集群?
    • 正确。在唯一 CI 的情况下,唯一标识符不是必需的,因此没有什么可以用尽的。不用过多透露,这张表的PK和聚类键是不同的。考虑一个成员表,其中 PK 是 MemberID,CI 是 GroupID。这个想法是主要访​​问路径是 GroupID,所以当我要求 GroupID=12345 时,所有这些行在物理上都彼此靠近。上面描述的反模式类似于“当我们添加一个还没有组的成员时,将它们放在这个表中,但 GroupID=-1”。
    【解决方案2】:

    如果聚集索引不是唯一的,则 SQL Server 会在内部创建 Uniquifier 以使该记录具有唯一性。我将尝试用一个例子来解释:

    CREATE TABLE Test2 (Col1 INT, Col2 INT)
    
    CREATE CLUSTERED INDEX idxClustered ON Test2 (Col1) 
    CREATE NONCLUSTERED INDEX idxNonClustered ON test2 (Col2) 
    

    这里的cluserered index不是唯一的

    INSERT INTO Test2 VALUES (1,1), (2,2) 
    INSERT INTO Test2 VALUES (3,3)
    INSERT INTO Test2 VALUES (3,3)
    
    --Get the Page Number of the Non Clustered Index
    DBCC IND (Test, Test2, -1)
    
    --Examine the Results of the Page
    --Not to run in production
    DBCC TRACEON (3604); 
    DBCC PAGE(Test, 1, 3376, 3); 
    

    您将看到具有相应唯一性值的 Uniquifier 键...如果您的聚集索引是唯一聚集索引,那么它将没有该 Uniquifier 属性。

    【讨论】:

      【解决方案3】:

      **usr* 有一篇好文章值得一读。我将在此处从 Microsoft 文档中添加。

      首先,Clustered-Indexes 并不孤单。老实说,这个名称本身有些令人困惑(Structured-IndexesDisk-IndexesSQL 中可能会更好)。

      请参阅MSDN 的官方文档。我的任何改动都用斜体

      聚集索引是表的磁盘上结构。这意味着这些值指向一个物理位置。这就是为什么当您移动表时需要重新创建索引,因为物理位置已被更改。

      集群

      • 聚集索引对表或视图中的数据行进行排序和存储 基于它们的关键值。这些是索引中包含的列 定义。每个表只能有一个聚集索引,因为 数据行本身可以仅按一种顺序排序。

      • 表中的数据行按排序顺序存储的唯一时间是 当表包含clustered index 时。当一个表有一个 聚集索引,该表称为clustered table。如果一个表有 没有聚集索引,它的数据行存储在一个无序的结构中 称为heap

      非集群

      • 非聚集索引具有与数据行分开的结构(类似于指针,这是占用物理磁盘空间一小部分的数据的逻辑排序)时间>。

      • 非聚集索引包含非聚集索引键值和每个 键值条目对包含该键的数据行有一个 pointer 价值。

      • 从非聚集索引中的索引行指向数据行的指针 称为row locator。行定位器的结构取决于 数据页是存储在heap 还是clustered table (认为是有序的)

      • 对于heap行定位符是指向行的指针
      • 对于clustered table行定位符是聚集索引键

      抽象视图

      1. 创建的表不一定是聚簇(有序)表。
      2. 索引不一定必须是唯一的。它是表格的抽象视图。
        • 唯一意味着一个值或一组值不会重复。如果您希望强制执行此操作,您可以通过索引添加约束(即UNIQUE CLUSTERED INDEX)或CONSTRAINT,例如PRIMARY KEY,如果您希望在表结构本身中进行管理。
      3. 您可能有多个唯一索引,因为只要这些值以逻辑表示,它们就不会与另一个行指针共享相同的值。

      假设您在给定表中有 A、B 和 C 列。

      A 列是使用UNIQUE CLUSTERED INDEX 创建的。这意味着 A 已经具有可执行的 UNIQUE 约束(如 PKUNIQUE CONSTRAINT)或已明确声明

      A 列组 {B,C} 可以是唯一索引,只要 B 和 C 永远不会一起重复。同样,理论上您可以使用组 {A}{B,C}{A,C}、每一个都是独一无二的。回想一下,索引是数据的逻辑排序,因此它们可能不会具有相同的逻辑值(因此是唯一的)。

      HOWEVER:除非数据类型、约束(包括 INDEX 约束)或表结构对 COLUMN 强制执行唯一约束,否则不应假设索引是唯一的。此外,如果有多个行包含相同的NULL 值组合,则无法创建UNIQUE 索引,因为SQL Server 会将它们视为相同的值(NULL 未知)。

      SQL Server 是否会使用您的索引,无论是否唯一?那是另一个故事,取决于很多事情。但希望这篇文章对您有所帮助。

      来源: MSDN - Clustered and Nonclustered Indexes Described

      【讨论】:

      • 我更喜欢“主存储”而不是“聚集索引”和 b-tree 作为 CI 和 NCI 的术语。 SQL Server 现在可以识别多种存储格式:堆、b 树、列存储、hekaton。我认为应该是create primary/secondary btree/heap/columnstore/hekaton index ...。每个表都应该从一个主堆开始。那将是一个不错的模型。我们永远不会得到这个:)
      【解决方案4】:

      聚集索引不必是唯一的。但是,一张表只能有一个聚集索引,因为聚集索引实际上决定了磁盘上表行的物理顺序(但我觉得说聚集索引表是令人困惑的数据本身,即使它们彼此紧密相连)。

      HERE 是一篇关于非唯一聚集索引的好文章。即使索引是整行数据,你肯定可以有重复的行(没有 PK),这等同于重复的聚集索引节点。

      【讨论】:

      • CI 和 NCI 之间实际上没有物理区别。这是人为的区别,“行的物理顺序”不是一个定义明确的术语。更好的思维模型是将所有索引视为(部分)但相等的数据副本。
      • @usr 哇,什么?根据谁?物理只是意味着它们的组织方式……我们不是在谈论机器级别的组织,而是 SOL Server 读取它们的顺序。非聚集索引是一种逻辑排序。为什么你认为一张表上只能有一个聚集索引?因为微软很有趣?
      • 这是一个简化很多事情的设计决策。将数据的副本之一指定为“主要”可以使模型更容易。如果您有一个 CI 和一个包含所有列的 NCI,那么您会将相同的数据存储两次。如果两者中的键列相同,则甚至没有物理差异(这很重要)。查询优化器也不关心 b 树是 CI 还是 NCI。基本概念是表数据可以以多种格式存储,有利于不同的访问。没有内在的规则强制其中一个副本是特殊的。
      • @clifton_h 因为数据只有一个主副本,所以只能有一个 CI。聚集索引的叶级别的行 表行,因此将任何意义附加到 CI 行与表行的顺序相同是没有意义的,因为它们都是完全相同的东西.聚集索引的叶级别的行不一定按任何特定顺序存储,例如按聚集索引键排序。不幸的是,在线书籍中的一些措辞对此产生了错误的印象。更多详情stackoverflow.com/a/24470091/73226
      • @Martin.Smith 我的习惯是重新检查我的消息来源,我很高兴我做到了。 MSDN 确认 CI 是一个On-disk 数据结构。只能有一个,因为它是一个物理订单。 NCI 有一个row-pointer,它是数据位置的逻辑表达式。 Structured and Nonstructured Indexes - MSDN
      猜你喜欢
      • 2011-04-17
      • 2014-11-04
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-18
      • 1970-01-01
      • 2012-12-21
      相关资源
      最近更新 更多