将主 id 字段更改为 sql server 中默认的 uniqueidentifier GUID

zey*_*zey 0 sql-server primary-key uniqueidentifier sql-server-2008-r2 alter-table

我有带有 id(primary key - int) 和身份规范 (1,1) 的表。
我想将此列更改为uniqueidentifier默认值 ( newid()) 。

我尝试过的是

ALTER TABLE myTable ALTER COLUMN id uniqueidentifier default NEWID();  
Run Code Online (Sandbox Code Playgroud)

但我收到了这条消息

Incorrect syntax near the keyword 'default'.
Run Code Online (Sandbox Code Playgroud)

此外,我的数据库的每个表都有id主键列,我想通过循环或其他方式将它们更改uniqueidentifier为默认值。newID()

Sol*_*zky 5

我的数据库的每个表都有 id 主键列,我想将它们更改为 nvarchar(36) 并使用默认的 newID()

不。您不想做出这样的改变,因为这样做不会带来任何好处。

您当前的 PK(我认为是集群)是:

  1. 紧凑:每行 4 字节
  2. 高效:比较是简单的 4 字节值到 4 字节值(即二进制比较)
  3. 最小化碎片:新值是连续的并添加到表的末尾。

您想要将其更改为集群 PK,即:

  1. Wide:每行72 字节(GUID 为 16 字节,十六进制值的字符串形式为 32 个字符,加上 4 个破折号,总共 36 个字符,UTF-16 / 为 72 字节NVARCHAR)。
  2. 效率低下:如果使用UNIQUEIDENTIFIER它并没有那么糟糕,因为它仍然是二进制比较,就像 with 一样INT,但它是 16 个字节,INT而不是 4 个字节。将其存储为字符串现在是 36 个字符的比较(36 个字节 inVARCHAR和 72 个字节 in NVARCHAR)这比 16 字节慢UNIQUEIDENTIFIER。最后,大多数将 GUID 存储为字符串的人忘记使用二进制排序规则(例如Latin1_General_100_BIN2)来至少进行逐字节比较,因为不需要语言规则。使用不区分大小写的排序规则,甚至区分大小写的排序规则,肯定会更慢,因为它将应用基于区域设置的语言规则。
  3. 高度碎片化:新值到处都是,导致页面拆分大大增加。如果您减少FILLFACTOR页面拆分的数量,那么您也会降低索引的性能,因为索引分布在大量页面上。

请记住此更改对下游的负面影响,原因如下:

  1. 聚集索引键被复制到非聚集索引中。再次假设该 PK 是聚集的,则该表上的每个非聚集索引都将复制该 72 字节值。这个表上的三个非聚集索引是72字节*3=216字节加上原来的聚集索引的72=288字节。另一方面,当前的大小INT只有 4 个字节 * 3 = 12 个字节加上原来的 4 = 16 个字节。每行。

  2. PK 通常由 FK 使用,FK 是将 PK 复制到一个或多个其他表中。每行 72 字节仅用于此表。如果此 PK 在其他 2 个表中用作外键,则为 72 字节 * 2 = 144 额外字节。另一方面,当前INT只有 4 字节 * 2 = 8 字节。

    FK列是否有索引?如果是,则又是 72 个字节,而不是 4 个字节。

  3. 数据页在其行可以被读取和使用之前被加载到内存(即缓冲池)中。较大的行和/或较低的FILLFACTOR行意味着需要更多的数据页来保存这些行。这意味着需要更多的时间将它们从磁盘读取到内存中,显然它们需要更多的内存。此需求与其他查询、计划缓存等竞争。

如果您需要一个 GUID 来获得外部系统知道的值,只需添加一UNIQUEIDENTIFIER列并为其建立索引即可;然后您可以查找它以获取INT用于所有其他 JOIN 等的值。并且,如果必须将其存储为字符串,则使用VARCHAR给定的唯一字符是A, B, C, , D, E,F-(NVARCHAR是完全不必要的浪费空间)确保指定二进制排序规则(以 结尾_BIN2)。

但不要改变当前的结构。


关于问题的更新,其中澄清了要更改为的预期数据类型是 trueUNIQUEIDENTIFIER而不是NVARCHAR(36):总体建议不会从“不这样做”改变。虽然UNIQUEIDENTIFIER是一个比NVARCHAR(36)(更小并且比较是二元的)更好的选择,但它实际上只是一个“不太糟糕”的选择,而不是“更好/好的选择”。