为什么SQL Server不支持无符号数据类型?

111

我特别考虑无符号 int

以下是一个实际的例子:当标识列达到最大值时,你应该怎么做?可以使用 BigInt(占用 8 字节存储空间而不是 4),或者重构应用程序以支持负整数,甚至按照这篇答案中所示,创建自己的规则;但这两种选项都不是最佳的。

UInt 将是一个理想的解决方案,但 SQL Server 并没有提供它(而 MySQL 则提供了)。

我明白无符号数据类型不是 SQL 标准(SQL-2003)的一部分,但对我来说仍然显得很浪费。

为什么(在 SQL Server 或标准中)不包含这些内容的原因是什么?


11
请问SQL Server设计团队……另外:你真的会用尽甚至2亿个INT自增值吗?真的吗?如果你有超过20亿行任何类型的数据需要处理,我敢打赌你可以节省一些磁盘空间并使用BIGINT作为自增ID。 - marc_s
6
你的意思是什么,marc_s?那只是连续50年每800毫秒插入一次,你的表格没有那种活动吗? :) - Mike M.
35
@Mike M:并非我们所有人都在做米老鼠应用程序...在不到2年的时间内,我们已经使用了30亿个大整数(bigint)。峰值每秒超过2000行。 - gbn
6
@gbn 我并不是想表达没有人有这样的负载。然而,正如之前所说的那样,如果你的负载每秒超过2000行,那么额外的2B并不能帮助你解决问题。 - Mike M.
18
如果我在处理拥有20亿行数据表的系统时,我可能会关注存储空间的浪费情况,并且会留意索引页面的大小和索引扫描性能。在这种情况下,我希望不浪费空间。@Mike M 和 @marc_s,请注意。 - Romhein
显示剩余9条评论
8个回答

82

您可以使用-2,147,483,648作为种子值。

Identity(-2147483648, 1)

11
哈哈,我非常喜欢这个答案。虽然我不确定我会实施它,但它确实解决了一半的ID未被使用的问题。 - Kevin
好的解决方案...不是很棒...但是解决了问题...将最小值设为零(0)...有效地使用开尔文温标 ;) - GoldBishop

79
如果我猜的话,我会说他们试图避免类型的增加。一般来说,无符号整数不能做有符号整数所不能做的事情。至于当你需要一个在2147483648和4294967296之间的数字时,你可能应该使用一个8字节整数,因为这个数字最终也会超过4294967296。

4
我想这应该是我们能够得到的最接近答案的回答了。谢谢。 - Romhein
11
如果“类型的增加”可以节省一些空间/金钱,那么为什么你认为这可能是件坏事。 - Samuel
1
按值获取行的速度也会变慢(即ORDER BY ABS(Id)),特别是如果该列是聚簇主键。例如,使用32位Unix时间戳通常是缩减标准SQL日期时间4个字节的方便方法。 - vgru
今天我面临的任务是将以字符串形式直接存储在SQL Server中的32位无符号数字的字节序进行转换。如果SQL Server接受无符号整数,使用BINARY(4)和reverse将会很容易实现... - Loudenvier

57

我在Microsoft Office Dev Center上找到了类似的问题

Jim Hogg(程序经理)的回复对添加无符号整数有一些利弊。最大的缺点是实现隐式类型转换的规则变得非常棘手。

请求已被关闭,标记为“不会修复”。


链接已经失效,所以我无法阅读原始答案。但我相信问题不在于它是一场噩梦,而是没有一个标准来说明如何做到这一点。例如,他们可以像MySQL一样做(我认为其他DBMS不支持UNSIGNED),但如果另一个DBMS添加了符号支持,他们可能会使用不同的规则。转换设计是一个重要的问题。JavaScript就是一个例子,当它没有被认真对待时会发生什么。 - Federico Razzoli
更新链接 - Jim Hogg 对 MSSN 办公开发者论坛的评论。 - Anthony K
我对无符号整数类型有强烈需求,因为它们更准确地表示所存储数据的“本质”。我完全理解隐式转换的担忧,但这不应该是一个问题,因为不应允许从有符号类型隐式转换为任何无符号类型,这可以很好地解决这个问题(我假设 ISO SQL 要求这样做?)。最终,我的沮丧之处在于,作为应用程序开发人员,我们被告知尽可能接近业务领域来建模数据,但然后我们被我们的工具束缚住了,无法做到这一点。 - Dai

2
他们不支持SIGNED和UNSIGNED关键字,因为它们不是标准。在SQL标准中,所有数值类型都是有符号的。
UNSIGNED(和默认的SIGNED)是MySQL扩展,可以用于在相同的字节数中存储更高的无符号数字,并禁止负数。

1
在SQL标准中,所有数字类型都是有符号的 - 是的,但ISO SQL与日常数据建模和应用程序开发的挑战极其不一致。为了交付可用的东西,违反ISO SQL规范成为绝对必要的。 - Dai
UNSIGNED类型是理想的,但并不是那么重要。大多数DBMS不支持它们,这使得类型转换(因此在不同类型之间进行操作)更加简单。如果您想避免负数,请添加CHECK约束。 - Federico Razzoli

1

您可以始终使用 DECIMAL。巨大的十进制数 - DECIMAL(38, 0)。应该足够一个月或两个月的使用...

CREATE TABLE IdentityTest
(
    Id DECIMAL(38, 0) IDENTITY,
    Name NVARCHAR(200)
)

INSERT INTO IdentityTest VALUES('John'),('Peter'),('Tom')

SELECT * FROM IdentityTest

DROP TABLE IdentityTest

这将产生以下结果:

编号 姓名
1 约翰
2 彼得
3 汤姆

0

在SQL Server中,有一些情况需要使用无符号数字。例如,可能需要将二进制值的等效值存储为整数。在这种情况下,对于32位二进制值,需要使用64位bigint而不是32位int数据类型。


0

以32位(8字节)整数为例。 32位整数的范围是从-2^31到2^31-1。 需要31位来记录您分配的值,只需要1位来记录值的符号。

所以你的问题的答案是“不必要的”。即使您分配的每个值都是正数,每个值仅浪费1位。创建一个新的数据类型仅保存每个值的1位并不是优化存储空间的好方法。


1
有时候,除了存储空间之外还有其他原因。如果我想在程序中使用无符号值(许多语言出于良好的原因都支持这种方式),我不应该必须执行转换才能检索/存储这些值到数据库中。然后查看原始数据的人必须手动或心理调整值以真正可视化程序中的状态。可用性和实用性是非常重要的因素,在这种情况下,SQL Server团队似乎完全忽略了它们。 - IAmJersh
如果你需要从INT跳转到BIGINT仅仅是因为INT不支持无符号,那么浪费的是每行4个字节,而不是1个比特。 - undefined

0

将您的数据库设置为最小标识Identity(-2147483648, 1)

然后在加载到您的.net UInt64变量时,添加2147483648。然后 -2147483648变成0 -1000000000变成1147483648

  • 但是在大多数情况下,内部密钥不应该暴露给客户端,我通常使用一个单独的密钥,可以是任何东西,比如“ABCKey1”

然而,我同意99%的系统中数据类型已经足够大了。如果您真的需要更多,可以使用GUID - 但是对于索引来说这很糟糕,除非您使用下一个连续的GUID。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接