Uniqueidentifier与IDENTITY vs. Material Code - 哪个是主键的最佳选择?

时间:2013-12-09 05:31:31

标签: sql-server database database-design sql-server-2012

哪一个是SQL Server中主键的最佳选择?
有一些示例代码:

Uniqueidentifiers

e.g。

CREATE TABLE new_employees
   (employeeId   UNIQUEIDENTIFIER      DEFAULT NEWID(),
   fname      VARCHAR(20) )
GO
INSERT INTO new_employees(fname) VALUES ('Karin')
GO

Identity columns

e.g。

 CREATE TABLE new_employees
 (
  employeeId int IDENTITY(1,1),
  fname varchar (20)
 );

 INSERT new_employees
    (fname)
 VALUES
    ('Karin');

[材料代码](或商业代码,材料的标识。例如客户标识符)

e.g。

CREATE TABLE new_employees(
    [ClientId] [varchar](20) NOT NULL,
    [fName] [varchar](20) NULL      
 )

 INSERT new_employees
    (ClientID, fname)
 VALUES
    ('C0101000001',--customer identifier,e.g.'C0101000001' a user-defined code.
     'Karin');

请给我一些建议,从三种类型的标识列或其他选项中选择主键。

谢谢!

4 个答案:

答案 0 :(得分:21)

GUID似乎是您主键的自然选择 - 如果您真的必须,您可能会争辩将其用于表的PRIMARY KEY。我强烈建议不要使用GUID列作为群集密钥,默认情况下SQL Server会执行此操作,除非您明确指出到。

你真的需要分开两个问题:

  1. 主键是一个逻辑结构 - 唯一且可靠地标识表中每一行的候选键之一。这可以是任何事情,真的 - INTGUID,字符串 - 选择对您的方案最有意义的内容。

  2. 群集密钥(在表上定义“聚簇索引”的一列或多列) - 这是与物理存储相关的东西,并且在这里,一个小而稳定,不断增加的数据类型是您的最佳选择 - INTBIGINT作为您的默认选项。

  3. 默认情况下,SQL Server表上的主键也用作群集键 - 但这不一定是这样!我个人看到在将先前基于GUID的主/群集密钥分解为两个单独的密钥(GUID上的主(逻辑)密钥和单独的{上的集群(排序)密钥)时可以获得巨大的性能提升{1}}列。

    作为Kimberly Tripp - 索引女王 - 以及其他人已多次声明 - 由于群集密钥不是最佳的INT IDENTITY(1,1)随机性,它将导致大量的页面和索引碎片,并导致一般性能不佳。

    是的,我知道 - 在SQL Server 2005及更高版本中有GUID - 但即使这样也不是真正完全顺序的,因此也遇到与newsequentialid()相同的问题 - 只是少了一点显着如此。

    然后还有另一个需要考虑的问题:表格上的聚类键也会被添加到表格中每个非聚集索引的每个条目上 - 因此你真的想确保它尽可能小。通常,对于绝大多数表来说,具有2亿行的GUID应该足够 - 并且与作为群集密钥的INT相比,您可以在磁盘上保存数百兆字节的存储空间。服务器内存。

    快速计算 - 使用GUIDINT作为主要和群集密钥:

    • 具有1'000'000行的基表(3.8 MB对15.26 MB)
    • 6个非聚簇索引(22.89 MB vs. 91.55 MB)

    TOTAL:25 MB vs. 106 MB - 这只是在一张桌子上!

    更多值得思考的东西 - 金佰利特里普的优秀作品 - 阅读,再读一遍,消化它!这是SQL Server索引福音,真的。

    除非你有非常好的理由,否则我会争论使用GUID几乎每个“真实”数据表作为其主键的默认值 - 它是唯一的,它是稳定的(从不改变),它是狭窄的,它会不断增加 - 你希望在群集密钥中拥有所有良好属性,以便快速可靠地执行SQ​​L Server表!

    如果您有一些“自然”键值也具有所有这些属性,那么您也可以使用它而不是代理键。但两个可变长度的字符串max。在我看来,每个20个字符都不符合这些要求。

答案 1 :(得分:5)

<强> IDENTITY

赞成

  1. 占地面积小;
  2. 最佳连接/索引性能(例如,对于时间范围查询,最近插入的大多数行将在有限数量的页面上);
  3. 对数据仓库非常有用;
  4. 操作系统的本机数据类型,易于使用所有语言;
  5. 易于调试;
  6. 自动生成(通过SCOPE_IDENTITY()检索而不是分配);
  7. 不可更新(虽然有些人认为这是一个缺点,奇怪的是)。
  8. CONS

    1. 无法可靠地预测&#34;按应用程序 - 只能在INSERT之后检索;
    2. 在多服务器环境中需要一个复杂的方案,因为在某些形式的复制中不允许使用IDENTITY;
    3. 如果没有明确设置为PRIMARY KEY,则可以复制
    4. 如果表上有聚集索引的一部分,则可以创建插入热点;
    5. 专有且不直接携带;
    6. 在单个表中唯一唯一;
    7. 可能会出现间隙(例如,使用回滚交易),这可能会导致鸡只小型警报。
    8. <强> GUID

      赞成

      1. 因为它们{或多或少}保证是唯一的,所以多个表/数据库/实例/服务器/网络/数据中心可以独立生成它们,然后合并而不会发生冲突;

      2. 某些复制形式需要
      3. ;

      4. 可以在数据库外部生成(例如,通过应用程序);
      5. 分布式值可防止热点(只要您不对此列进行聚类,这可能会导致异常高的碎片)。
      6. CONS

        1. 更宽的数据类型导致索引性能下降(如果是群集,每个插入几乎保证“脏”不同页面),并且存储要求增加;
        2. 调试很麻烦(其中userid = {BAE7DF4-DDF-3RG-5TY3E3RF456AS10});
        3. 可更新(需要传播更改,或完全阻止活动);
        4. 对某些环境中的时间回滚敏感(例如夏令时回滚);
        5. GROUP BY和其他设置操作通常需要CAST / CONVERT;
        6. 并非所有语言和环境都直接支持GUID;
        7. 没有像SCOPE_GUID()这样的语句来确定生成的值,例如by NEWID();

答案 2 :(得分:2)

在设计表时,您需要考虑的一件事是,您是否需要复制,分片或以其他方式将数据从一个地方移动到另一个地方。也许数据是由其他应用程序生成的,需要与您的数据保持同步。一个例子是移动应用程序,它创建数据然后与服务器同步。如果这样的事情是真的或可能是真的那么UNIQUEIDENTIFIER将是用作主键的好选择。

UNIQUEIDENTIFIER数据类型在用作聚簇索引时性能很差。是的,您可以使用newsequentialid(),但如果在其他设备上生成这些值,则无法帮助您。共识似乎是聚簇索引最好与顺序和窄数据类型一起使用,如INTBIGINT

如果您不关心存储空间问题,那么您可以尝试同时使用IDENTITY 群集密钥UNIQUEIDENTIFIER主键。创建群集密钥 IDENTITY列,并将其用于聚簇索引(但不能用作主键)。插件仍将按顺序进行,并且满足了它作为窄数据类型的要求。现在,您可以使用UNIQUEIDENTIFIER作为主键。这将允许您在需要时移动,复制和/或分片数据。

群集密钥除了保持插入顺序之外没有其他目的,并且是查找给定查询的数据时所有其他非群集索引指向的内容。 群集密钥完全丢弃,并且可以在移动,复制和/或分片数据时重新生成,因为唯一性由UNIQUEIDENTIFIER主键处理。

这是一篇很棒的文章,演示了在为聚集索引使用IDENTITY和UNIQUEIDENTIFIER时内部会发生什么。

答案 3 :(得分:1)

GUID很大,但具有在任何地方都是唯一的优势:这个或那个表,或者那个,如果你有GUID,那么其他一切都是可知的。如果这对你有用,那么很好,但你会在头顶上支付,并继续支付,支付和支付....

材质代码仅适用于较小的不可变键,如颜色或分类代码等。 R将始终为红色,G为绿色,为一个字节等。

当可能没有材料代码,或者自然键由多个材料代码组成在一起,或者自然键已经由其他标识列和/或GUID或自然键组成时,标识列会自行生成是可变的。是的,您可以使用GUID,但整数列在所有方面都更有效。

SQL 2012中提供的另一个选项是序列,类似于数据库级标识列。这是GUID和标识列之间的一个很好的中途,在某种意义上说,序列可以在许多表中使用,因此从给定的值,不仅行是可知的,但表也是 - 但你仍然可以使用如果您认为对您的数据足够,则为INT或BIGINT(或SMALLINT!)。这对于某些目的来说有点漂亮,有点像OO世界中的对象id。

请注意,许多或轻量级ORM要求表具有单个列主键,最好是整数列,并且除了INT IDENTITY PK之外的其他任何内容都可能无法正常运行。