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


68

我被教导不要在Id表的标识列中使用该名称,但是最近我还是一直在使用它,因为它简单,简短并且对数据的真实性具有很强的描述性。

我见过有人建议Id给表名加上前缀,但这似乎对编写SQL查询的人(或者如果您使用的是诸如Entity Framework的ORM,则是程序员)做更多的工作,尤其是在较长的表名上,例如CustomerProductId要么AgencyGroupAssignementId

我们雇用了一个第三方供应商来为我们创建一些产品,实际上Ident是为了避免使用,而将其所有标识列都命名为Id。最初,我以为他们这样做是因为它Id是一个关键字,但是当我查看它时,发现它Id不是SQL Server 2005中的关键字,这就是我们正在使用的关键字。

那么为什么人们建议不要在Id标识列中使用该名称?

编辑:为澄清起见,我不是在问要使用哪种命名约定,也不是要在参数中使用一种命名约定。我只想知道为什么建议不要将其Id用于标识列名称。

我是一个程序员,而不是dba,对我而言,数据库只是存储我的数据的地方。由于我通常构建小型应用程序,并且通常使用ORM进行数据访问,因此使用Identity字段的通用字段名称要容易得多。我想知道这样做会导致我错过什么,以及是否有确凿的理由让我不这样做。


10
这里BF bunfight已经:programmers.stackexchange.com/q/114728/5905得到吸了进去...:我们几个(我读)的
GBN

真的有这样的规则禁止使用“ id”作为标识列的名称吗?ActiveRecord是Ruby on Rails的标准ORM,按照约定完全可以做到这一点。 ar.rubyonrails.org
200_success 2012年

1
@ 200_success在数据库级别,是。该数据库站点,而不是ORM站点;)
JNK 2012年

Answers:


46

表名前缀有很好的理由。

考虑:

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

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

我们DELETE要从TableA两个表中都存在的记录中提取数据。很简单,我们将要做一个INNER JOIN

DELETE a
FROM 
  TableA A
INNER JOIN 
  TableB B
    ON b.id = B.id

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

如果字段已命名TableAId,则TableBId不可能(Invalid field name TableAid in TableB)。

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

这也使很明显,字段长来自带有多个JOINs的长查询。


10
因此,基本上,这是防止错误的命名约定?我认为使用(imo)比使用(imo)更令人讨厌的命名方案更好begin transactioncommit transaction这是一种更好的做法
Rachel

13
@Rachel:它是1.清晰2.避免不必要的列别名3.允许JOIN..USING 4.惹恼PHP猴子谁在单个对象的工作,而不是套
GBN

4
@Rachel如果您在编写查询时没有注意到错误,并且在执行查询之前,就不太可能在提交之前注意到它。这些事情发生了,为什么要使其更有可能?
安迪

7
@Andy SELECT在运行之前,我总是做一个查找我的记录的操作DELETE,一旦运行了语句,我总是在提交之前验证行计数是否是我期望的。
雷切尔2012年

5
@Rachel很高兴您有适合自己的东西。你可以让每个人都这样做吗?
安迪

36

通常,这是为了防止外键成为巨大的痛苦。假设您有两个表:Customer和CustomerAddress。两者的主键都是一个名为id的列,这是一个标识(int)列。

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

这导致了两个问题。首先,您必须始终记住何时调用列“ id”以及何时调用列“ customer_id”。如果弄乱了它,则会导致第二个问题。如果您有一个带有十几个联接的大型查询,并且没有返回任何数据,请玩一下《哪里是沃尔多》并找出这种错别字:

ON c.id = ca.id

哎呀,应该是ON c.id = ca.customer_id。最好还是用描述性的方式为您的身份列命名,这样就可以了ON c.customer_id = ca.customer_id。然后,如果您在某个地方不小心使用了错误的表别名,customer_id将不会是该表中的一列,并且您将得到一个不错的编译错误,而不是空结果和随后的代码斜视。

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


27

这是关于从约定中获得的优点的所有答案的摘要,这些优点是不对所有主键使用通用名称:

  • 错误少,因为标识字段的名称不同

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

  • 更清晰的列名。

    如果查看名为的列CustomerId,您将立即知道该列中的数据。如果列名是通用名称,例如Id,那么您还需要知道表名,以了解列包含的数据。

  • 避免不必要的列别名

    现在,您可以编写SELECT CustomerId, ProductId从联接查询CustomersProducts,而不是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列fooTableIdbarTable外键fooTableId,那么您的查询可以这样构造:

SELECT fooTableId, fooField1, barField2 FROM fooTable INNER JOIN barTable USING (fooTableId)

与其他方法相比,它不仅可以节省键入内容,而且可读性更高:

SELECT fooTable.Id, fooField1, barField2 FROM fooTable INNER JOIN barTable ON (fooTable.Id = barTable.foTableId)

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     |

对其进行规范化将产生两个表:

作者:

| Author_Nickname | Author_Email |
+-----------------+--------------+
| dave            | dave@x.com   |
| sophie          | s@oph.ie     |

发布

| Author_Nickname | Post_Title | Post_Body |
+-----------------+------------+-----------+
| dave            | Blah       | Bla bla   |
| dave            | Stuff      | I like    |
| sophie          | Lorem      | Ipsum     |

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

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

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


7

我们聘用的一家第三方供应商为我们创建了一个产品,实际上将其所有标识列都命名为Ident,只是为了避免使用Id。

如果最终在所有表上都使用“ Ident”,则使用“ Ident”代替“ Id”并不能解决任何问题。

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

最好在表名前加上模块名,以防止可能的名称空间冲突。

从这个角度来看,可以使用CustomerProductId和AgencyGroupAssignmentId。是的,它很冗长。您可以缩短它的时间,但是那时最需要注意的是跟随您的开发人员是否会理解您的意思。以冗长的表名开头的ID不应对其模棱两可。而且(对我而言)比节省一些击键更为重要。


7

我将列命名为CustomerID而不是ID,因此无论何时键入

FROM dbo.Customers AS c JOIN dbo.CustomerOrders AS o

SQL提示立即建议以下内容

ON c.CustomerID = o.CustomerID 

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


5

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

通常,如果表中有一个标识字段,则它是您的主键。如果两个表中的主键均为id,您将如何构建一个带有指向父表的外键的子表?

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


5

基本上出于相同的原因,您通常不将参数命名为parameter1,parameter2 ...这是准确的,但不是描述性的。如果看到TableId,则可以安全地假定它用于保存Table的pk,而与上下文无关。

至于使用Ident的人,只要在Ident和Id use Id之间进行选择,他就完全错了。Ident比ID更令人困惑。

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


3

使用前缀,以便可以在主键和外键上下文中使用相同的名称,以便可以执行natural join/ join ... using

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.