【问题标题】:Does adding a primary key cause restructuring of underlying data添加主键是否会导致基础数据重组
【发布时间】:2013-08-30 18:16:08
【问题描述】:

我正在将大量数据导入 SQL Server 数据库。源数据来自 PgSql(包括表 defs),我通过一些相当简单的正则表达式将其转换为 TSql。这会创建没有主键的表。

据我了解,缺少主键/聚簇索引意味着数据存储在堆中。

导入完成后,我按如下方式添加 PK:

ALTER TABLE someTable ADD CONSTRAINT PK_someTable PRIMARY KEY (id);

(注意缺少CLUSTERED 关键字)。现在发生了什么事?还是一堆?对主键查找有什么影响?这与添加标准索引真的有什么不同吗?

现在,改为按如下方式添加 PK:

ALTER TABLE someTable ADD CONSTRAINT PK_someTable PRIMARY KEY CLUSTERED (id);

我假设现在这会将表完全重组为基于行的结构,通过 PK 查找更有效,但插入特性不太理想。

我的假设正确吗?

如果我的导入按 PK 顺序插入数据,首先省略 PK 有什么好处吗?

【问题讨论】:

  • 你想插入行然后你想添加PK吗?
  • 我也可以,但考虑到数据量,我宁愿了解发生了什么,也不愿花 5-8 小时测试不同的场景。我可以在之前或之后添加键,但插入 按 PK 顺序发生。
  • 如果目标表是 HEAP,INSERT 应该更快。但是具有 PK(非集群)的 HEAP 表的整体(插入、更新、删除、选择)性能应该比集群表的性能差。看看这个SQL Server Best Practices Article。如果你导入大量数据,你应该看看table partitioning (SQL2005+)
  • 另见article

标签: sql sql-server sql-server-2008 primary-key clustered-index


【解决方案1】:

当你执行时

ALTER TABLE someTable ADD CONSTRAINT PK_someTable PRIMARY KEY (id);

如果someTable 上没有聚集索引,则 PK 将是聚集 PK。否则,如果在执行 ALTER .. ADD ... PRIMARY KEY (id) 之前存在聚集索引,则 PK 将是非聚集 PK。

-- 测试#1

BEGIN TRAN;
CREATE TABLE dbo.MyTable
(
    id INT NOT NULL,
    Col1 INT NOT NULL,
    Col2 VARCHAR(50) NOT NULL
);
SELECT  i.name, i.index_id, i.type_desc
FROM    sys.indexes i
WHERE   i.object_id = OBJECT_ID(N'dbo.MyTable');
/*
name index_id    type_desc
---- ----------- ---------
NULL 0           HEAP
*/
ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable PRIMARY KEY (id);

SELECT  i.name, i.index_id, i.type_desc
FROM    sys.indexes i
WHERE   i.object_id = OBJECT_ID(N'dbo.MyTable');
/*
name        index_id    type_desc
----------- ----------- ---------
PK_MyTable  1           CLUSTERED
*/
ROLLBACK;

-- 测试#2

BEGIN TRAN;
CREATE TABLE dbo.MyTable
(
    id INT NOT NULL,
    Col1 INT NOT NULL,
    Col2 VARCHAR(50) NOT NULL
);
SELECT  i.name, i.index_id, i.type_desc FROM    sys.indexes i WHERE i.object_id = OBJECT_ID(N'dbo.MyTable');
/*
name index_id    type_desc
---- ----------- ---------
NULL 0           HEAP
*/
CREATE CLUSTERED INDEX ix1
ON dbo.MyTable(Col1);

SELECT  i.name, i.index_id, i.type_desc FROM    sys.indexes i WHERE i.object_id = OBJECT_ID(N'dbo.MyTable');
/*
name index_id    type_desc
---- ----------- ---------
ix1  1           CLUSTERED
*/

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable PRIMARY KEY (id);

SELECT  i.name, i.index_id, i.type_desc FROM    sys.indexes i WHERE i.object_id = OBJECT_ID(N'dbo.MyTable');
/*
name       index_id    type_desc
---------- ----------- ------------
ix1        1           CLUSTERED
PK_MyTable 2           NONCLUSTERED
*/
ROLLBACK;

【讨论】:

  • 很好的答案。如果可以的话,我会 +2。谢谢。
  • 对我的上一期有什么想法吗?
【解决方案2】:

在 sql server 中,如果不存在聚簇索引,则主键默认为聚簇。聚集索引实际上意味着“索引”不是保存在单独的存储区域中(非聚集索引也是如此),而是索引数据与相应的常规表数据“穿插”在一起。如果您对此有所了解,您将意识到它们只能是 1 个集群索引。

聚集索引的真正优势在于数据靠近索引数据,因此您可以在驱动器头“在该区域”时同时抓取两者。当您正在处理的数据表现出引用的局部性时,聚集索引明显比非聚集索引快——当倾向于同时读取几乎相同值的行时。

例如,如果您的主键是 SSN,则除非您处理相对于 SSN 随机排序的数据,否则您不会获得很大的优势 - 尽管由于数据的接近性您确实会获得优势。但是,如果您可以通过 SSN 对输入进行预排序,那么集群键将是一个很大的优势。

所以是的,聚集索引确实会重新排序数据,以便与聚集索引混合。

【讨论】:

  • 由于数据库是多用户的,事情从来没有我说的那么简单。但是,在处理诸如发票之类的事情时,如果发票项目将发票编号作为托管索引的一部分,那么一次获取所有行项目通常会变得更快。
【解决方案3】:

感谢您对该主题的精彩演示!

上面的结论没有错误,但它显示的是索引的结构,而不是表的结构。我认为以下 SQL 将显示实际表的信息:

select 
    o.name, 
    o.object_id, 
    case 
      when p.index_id = 0 then 'Heap'
      when p.index_id = 1 then 'Clustered Index/b-tree'
      when p.index_id > 1 then 'Non-clustered Index/b-tree'
    end as 'Type'
from sys.objects o
inner join sys.partitions p on p.object_id = o.object_id
where o.name = 'MyTable';

你会看到 MyTable 是集群的:

name    object_id   Type
------- ----------- -------------------
MyTable 1237579447  Clustered Index/b-tree

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    相关资源
    最近更新 更多