选择 PK:VARCHAR(2) 还是 SMALLINT(2)?

Hum*_*All 3 database-design sql-server

今天我们讨论了以下问题:

巴西有 27 个州,每个州都有自己的缩写(就像美国一样)。所以我们有RJ里约热内卢、SP圣保罗、MG米纳斯吉拉斯州等等。

我们的一个程序员的建议,我们应该使用这些缩写(RJSPMG等)PKStates表中,我们正在计划添加到一个新的项目。

推断我们数据库的使用,我反驳了他的论点,说如果我们-有一天-将我们的服务扩展到其他国家,我们会遇到重复缩写的问题,例如:在美国SC,南卡罗来纳州和巴西都有我们已经到SC了圣卡塔琳娜州;对于MT,PA和也会发生同样的情况MA。基于这一点,我们同意应该有一ID列 as PK IDENTITY

现在,假设我们不将我们的服务扩展到其他国家并且只停留在巴西,我开始考虑使用 VARCHAR(2) 列作为 PK 的想法。在这种情况下,这听起来并不完全是个坏主意。是吗?为什么?在哪些情况下可以应用?是否应该考虑记忆以便从一种选择到另一种?

Sol*_*zky 8

在大多数情况下,我同意@Aaron 的方法,但这恰好是使用真正自然的密钥的少数实例之一:ISO 代码(特别是ISO 3166)。对于那些不熟悉 ISO 的人来说,用他们自己的话来说(来自主要的http://www.iso.org/页面):

我们是 ISO,即国际标准化组织。我们制定和发布国际标准。

ISO 3166-1描述了国家/地区代码。虽然有多种代码可供选择(2 个字符、3 个字符和数字),但 2 个字符代码是推荐的选择并且使用最广泛(包括大多数基于国家/地区的顶级域名)。

ISO 3166-2描述了每个国家的细分(例如州)。该标准分为基于国家的部分(例如巴西的ISO 3166-2:BR),代码为 1、2 或 3 个字母数字字符。

因此,您可以执行以下操作:

CREATE TABLE dbo.Country
(
  CountryCode CHAR(2) NOT NULL
                      COLLATE Latin1_General_100_BIN2
                      PRIMARY KEY,
  CountryName VARCHAR(50) NOT NULL
);

CREATE TABLE dbo.CountrySubdvision
(
  CountrySubdvisionCode VARCHAR(3) NOT NULL
                                   COLLATE Latin1_General_100_BIN2,
  CountryCode CHAR(2) NOT NULL
                      COLLATE Latin1_General_100_BIN2
                      CONSTRAINT [FK_CountrySubdvision_Country]
                                 FOREIGN KEY
                                 REFERENCES dbo.Country(CountryCode)
                                 ON UPDATE CASCADE,
  CountrySubdvisionName VARCHAR(50) NOT NULL,
  --  LocalizedSubdvisionName NVARCHAR(50) NOT NULL, -- ??
  CONSTRAINT [PK_CountrySubdvision]
             PRIMARY KEY (CountryCode, CountrySubdvisionCode)
);
Run Code Online (Sandbox Code Playgroud)

这里的想法是,您可以将CountryCodeCountrySubdvisionCode字段放在需要“状态”值的任何表中,并将 FK 放回dbo.CountrySubdvisionon (CountryCode, CountrySubdvisionCode)

虽然此方法根据国家/地区/细分使用 3 - 5 个字节,不如使用 2 字节紧凑SMALLINT,但它确实具有将人类可读/有意义的值放置在这些相关表中的优点。这可以轻松减少一定数量的 JOIN(当您只需要 2 个字符的代码时)并且可以(至少稍微)减少调试某些问题所花费的时间。

请注意,我_BIN2为两个“代码”字段指定了(即二进制)排序规则以帮助提高性能。即使有额外的几个字节,它也应该与比较两个SMALLINT值几乎一样快。唯一的缺点是你需要在添加所有大写的代码时保持一致,人们需要记住在过滤这些代码时他们需要使用所有大写。

另请注意,我ON UPDATE CASCADE在 FK on 中添加了该子句,dbo.CountrySubdvision以处理CountryCodeISO 更改a 的可能性(极不可能)。同样,在放置CountryCodeCountrySubdvisionCode字段的表上创建的 FK也需要设置为ON UPDATE CASCADE。这将传播到所做的更改CountryCodedbo.Country到具有两个字段的表。这还将传播CountrySubdvisionCode对这些相关表所做的任何更改。这也极不可能发生(特别是对于我们的大多数系统将处理的地方),尽管比对CountryCode.


更新

现在,假设我们不将我们的服务扩展到其他国家并且只停留在巴西,我开始考虑使用 VARCHAR(2) 列作为 PK 的想法。在这种情况下,这听起来并不完全是个坏主意。

只是为了说明这一点,如果使用这种方法(即 ISO 代码)来表示“州”/“省”和可能的国家,那么从技术上讲,您确实有能力开始处理单个国家的“州”。在这种配置中,您将只需删除dbo.Country表和CountryCode从外地(和相关的FK) dbo.CountrySubdvision。然后你只需要CountrySubdvisionCode放置在任何相关的表中。

现在,如果您以后发现需要扩展系统以处理其他国家/地区,那么您可以在那时执行以下步骤(这些步骤都不会更改任何CountrySubdvisionCode相关表中的任何现有值):

  1. 创建dbo.Country
  2. 填充dbo.Country表格
  3. 从引用的相关表中删除任何 FK dbo.CountrySubdvision
  4. 放下 PK dbo.CountrySubdvision
  5. 将该CountryCode字段添加到dbo.CountrySubdvision,使其成为迄今为止您一直使用的唯一国家/地区的 CountryCode 的NOT NULL一个DEFAULTDEFAULT如果您愿意,可以在此过程结束时将其删除)
  6. 重新创建PK就dbo.CountrySubdvision可以了(CountryCode, CountrySubdvisionCode)
  7. dbo.CountrySubdvisionCountryCode字段创建 FK on以引用dbo.Country
  8. 将该CountryCode字段添加到所有已有该CountrySubdvisionCode字段的相关表中,使其NOT NULLDEFAULT迄今为止唯一使用过的国家/地区的 CountryCode 一起使用(DEFAULT如果您愿意,可以在此过程结束时将其删除)
  9. 在这些相关表上为两个字段重新创建 FK 以引用该dbo.CountrySubdvision
  10. CountryCodedbo.CountrySubdvision相关表中删除字段上的默认约束