为什么人们不建议在标识列中使用名称“Id”?

Rac*_*hel 72 naming-convention identity

我被教导不要将名称Id用于我的表的标识列,但最近我一直在使用它,因为它简单、简短并且非常描述数据的实际内容。

我见过有人建议Id使用表名作为前缀,但这似乎让编写 SQL 查询的人(或程序员,如果您使用的是像实体框架这样的 ORM)做更多的工作,尤其是在更长的表名上,例如CustomerProductId或者AgencyGroupAssignementId

我们聘请的一位第三方供应商为我们创建了一些东西,实际上命名了他们所有的身份列,Ident只是为了避免使用Id. 起初我认为他们这样做是因为Id是一个关键字,但是当我查看它时,我发现它Id不是 SQL Server 2005 中的关键字,而我们正在使用它。

那么为什么人们建议不要使用Id标识列的名称呢?

编辑:澄清一下,我不是在问要使用哪种命名约定,也不是在询问使用一种命名约定而不是另一种命名约定的参数。我只想知道为什么建议不要使用Id标识列名称。

我是一名程序员,而不是 dba,对我来说,数据库只是存储数据的地方。由于我通常构建小型应用程序,并且通常使用 ORM 进行数据访问,因此标识字段的通用字段名称更容易使用。我想知道这样做我错过了什么,以及是否有任何真正好的理由让我不这样做。

JNK*_*JNK 48

表名前缀有很好的理由。

考虑:

TableA (id int identity, stringdata varchar(max))

TableB (id int identity, stringdata varchar(max))
Run Code Online (Sandbox Code Playgroud)

我们想DELETETableA两个表中都存在的记录中提取。很简单,我们只做一个INNER JOIN

DELETE a
FROM 
  TableA A
INNER JOIN 
  TableB B
    ON b.id = B.id
Run Code Online (Sandbox Code Playgroud)

....我们刚刚消灭了所有TableA. 我们无意中将 B 的 ID 与其自身进行了比较——每条记录匹配,每条记录都被删除。

如果字段已被命名TableAIdTableBId这将是不可能的 ( Invalid field name TableAid in TableB)。

就我个人而言,我对id在表中使用名称没有任何问题,但在它前面加上表名(或实体名称,如果TableA有人PeopleId也可以正常工作)确实是一个更好的做法,以避免意外地与错误的字段进行比较和吹有事。

这也使得在包含大量JOINs 的长查询中字段来自哪里变得非常明显。

  • @Rachel:这是为了 1. 清晰 2. 避免不必要的列别名 3. 允许 JOIN..USING 4. 惹恼在单个对象而不是集合中工作的 PHP 猴子 (14认同)
  • 所以它基本上是一种防止错误的命名约定?我认为使用“开始事务”和“提交事务”会比使用(imo)更讨厌的命名方案更好 (12认同)
  • @Andy 我总是在运行 `DELETE` 之前执行 `SELECT` 来查找我的记录,并且一旦我运行该语句,我总是在提交之前验证行数是否符合我的预期。 (8认同)
  • @Rachel 很高兴你有适合你的东西。你能让每个人都这样做吗? (5认同)
  • @Rachel如果您在编写查询时没有注意到错误,并且就在执行之前,您不太可能在提交之前注意到它。这种事情发生了,为什么让它更有可能发生? (4认同)

db2*_*db2 38

主要是为了防止外键成为一个巨大的痛苦。假设您有两个表:Customer 和 CustomerAddress。两者的主键是一个名为 id 的列,它是一个身份 (int) 列。

现在您需要从 CustomerAddress 引用客户 ID。显然,您无法命名列 id,因此您使用 customer_id。

这会导致几个问题。首先,您必须始终记住何时调用列“id”以及何时调用它“customer_id”。如果你搞砸了,就会导致第二个问题。如果您有一个包含十几个连接的大型查询,并且它没有返回任何数据,那么玩玩 Where's Waldo 并找出这个错字:

ON c.id = ca.id
Run Code Online (Sandbox Code Playgroud)

哎呀,应该是ON c.id = ca.customer_id。或者更好的是,描述性地命名您的标识列,因此它可以是ON c.customer_id = ca.customer_id. 然后,如果您不小心在某处使用了错误的表别名,则 customer_id 将不会成为该表中的列,并且您将得到一个很好的编译错误,而不是空结果和随后的代码眯眼。

当然,在某些情况下这没有帮助,例如如果您需要从一个表到另一个表的多个外键关系,但是将所有主键命名为“id”也无济于事。


Rac*_*hel 29

以下是有关不为所有主键使用通用名称的约定所带来的优势的所有答案的摘要:

  • 更少的错误,因为身份字段的名称不同

    您不能错误地编写一个连接 onB.Id = B.Id而不是 的查询A.Id = B.Id,因为标识字段永远不会被命名为完全相同。

  • 更清晰的列名。

    如果您查看名为 的列CustomerId,您会立即知道该列中有哪些数据。如果列名是类似的通用名称Id,那么您还需要知道表名才能知道该列包含哪些数据。

  • 避免不必要的列别名

    您现在可以SELECT CustomerId, ProductIdCustomersProducts, 而不是连接的查询中写入SELECT Customer.Id as CustomerId, Products.Id as ProductId

  • 允许JOIN..USING语法

    您可以使用语法连接表Customer JOIN Products USING (CustomerId),而不是Customer JOIN Products ON Customer.Id = Products.Id

  • 关键字在搜索中更容易找到

    如果您正在大型解决方案中寻找客户的身份字段,则搜索CustomerId远比搜索有用Id

如果您能想到此命名约定的任何其他优势,请告诉我,我会将其添加到列表中。

您是否选择为标识字段使用唯一或相同的列名取决于您,但无论您选择什么,请保持一致:)


小智 12

要从链接的问题中复制我的答案:

在某些情况下,在每张桌子上都贴上“ID”并不是最好的主意:USING关键字(如果支持的话)。我们经常在 MySQL 中使用它。

例如,如果您有fooTablewith columnfooTableIdbarTablewith foreign key fooTableId,那么您的查询可以这样构造:

SELECT fooTableId, fooField1, barField2 FROM fooTable INNER JOIN barTable USING (fooTableId)
Run Code Online (Sandbox Code Playgroud)

与替代方案相比,它不仅节省了打字的时间,而且更具可读性:

SELECT fooTable.Id, fooField1, barField2 FROM fooTable INNER JOIN barTable ON (fooTable.Id = barTable.foTableId)
Run Code Online (Sandbox Code Playgroud)


小智 9

在规范化数据库模式以限制冗余后,将表划分为具有已建立关系(一对一、一对多、多对多)的较小表。在此过程中,原始表中的单个字段可以出现在多个规范化表中。

例如,假设 Author_Nickname 上有唯一约束,博客数据库的非规范化形式可能如下所示。

| Author_Nickname | Author_Email | Post_Title | Post_Body |
+-----------------+--------------+------------+-----------+
| dave            | dave@x.com   | Blah       | Bla bla   |
| dave            | dave@x.com   | Stuff      | I like    |
| sophie          | s@oph.ie     | Lorem      | Ipsum     |
Run Code Online (Sandbox Code Playgroud)

标准化它会产生两个表:

作者:

| Author_Nickname | Author_Email |
+-----------------+--------------+
| dave            | dave@x.com   |
| sophie          | s@oph.ie     |
Run Code Online (Sandbox Code Playgroud)

邮政

| Author_Nickname | Post_Title | Post_Body |
+-----------------+------------+-----------+
| dave            | Blah       | Bla bla   |
| dave            | Stuff      | I like    |
| sophie          | Lorem      | Ipsum     |
Run Code Online (Sandbox Code Playgroud)

这里 Author_Nickname 将是 author 表的主键,以及 post 表中的外键。即使 Author_Nickname 出现在两个表中,它仍然对应一个信息单元,即。每个列名对应一个字段

在许多情况下,原始字段不能有唯一约束,因此使用数字人工字段作为主键。这不会改变每个列名仍然代表一个字段的事实。在传统的数据库设计中,单个列名对应单个字段,即使它们不是键。(例如,可以使用part.partnameclient.clientname而不是part.nameclient.name)。这就是存在INNER JOIN ... USING <key>NATURAL JOIN语法的原因。

然而,如今,随着 ORM 层在许多语言中都可用,数据库通常被设计为面向对象语言的持久层,其中在不同类中具有相同作用的变量被称为相同(part.nameclient.name,而不是part.partnameclient.clientname)。在这种情况下,我倾向于使用“ID”作为主键。


Aar*_*ron 8

我们聘请的一位第三方供应商为我们创建了一些东西,实际上将他们的所有身份列命名为 Ident 只是为了避免使用 Id。

如果“Ident”最终被用在所有表上,则使用“Ident”而不是“Id”并不能真正解决任何问题。

Drupal 站点上有一篇关于SQL 编码约定的好文章,指出了针对这种情况的良好实践:

用模块名称作为表名的前缀是一个很好的做法,以防止可能的命名空间冲突。

从这个角度来看,使用 CustomerProductId 和 AgencyGroupAssignmentId 是有意义的。是的,它非常冗长。你可以缩短它,但最重要的一点是关注你的开发人员是否会理解你的意思。以冗长的表名开头的 ID 不应该对它们的含义产生歧义。而且(对我而言)这比节省几次按键更重要。


A-K*_*A-K 7

我将我的列命名为 CustomerID 而不是 ID,所以每当我输入

FROM dbo.Customers AS c JOIN dbo.CustomerOrders AS o
Run Code Online (Sandbox Code Playgroud)

SQL Prompt 立即提示以下内容

ON c.CustomerID = o.CustomerID 
Run Code Online (Sandbox Code Playgroud)

它为我节省了几次按键操作。然而,我认为命名约定是非常主观的,因此我没有一种或另一种强烈的意见。


DFo*_*k42 5

这与您不会将所有 varchar 字段命名为“UserText”和“UserText1”之类的名称,或者您不会使用“UserDate”和“UserDate1”的原因相同。

通常,如果您在表中有一个标识字段,它就是您的主键。如果两个表中的主键都是 id,你将如何构建一个带有父表外键的子表?

不是每个人都同意这种方法论,但在我的数据库中,我为每个表分配了一个唯一的缩写。该表的 PK 将命名为 PK_[abbrv]ID。如果它在任何地方用作 FK,那么我将使用 FK_[abbrv]ID。现在我有零猜测的工作来弄清楚表关系是什么。


jmo*_*eno 5

基本上出于同样的原因,您通常不会将参数命名为 parameter1、parameter2...它是准确的,但不是描述性的。如果您看到 TableId,那么您可以放心地假设它用于保存 Table 的 pk,而不管上下文如何。

至于谁使用了 Ident,他完全没有抓住重点,在 Ident 和 Id 使用 Id 之间做出选择。Ident 比 Id 更令人困惑。

脱离上下文,可以假设 Id 是某个表的主键(除非 id 是 guid,否则不是非常有用),但 Ident 甚至没有告诉您(或至少我)。我最终会发现 Ident 是身份的缩写(以一种或另一种方式),但我花在弄清楚这一点上的时间将被浪费。