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

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

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

所以为什么人们建议不要在标识列中使用名称Id

编辑:为了澄清起见,我不是在问要使用哪种命名约定,还是在问使用一个命名约定而不是另一个命名约定的参数。我只想知道为什么建议不要在身份列名称中使用Id

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

评论

BF混战已经在这里:programmers.stackexchange.com/q/114728/5905我们中的一些人(读到:我)陷入其中...

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

@ 200_success在数据库级别,是。此数据库站点,而不是ORM站点;)

另外,对于SQL Server,请参阅dba.stackexchange.com/questions/124655/…,更具体地说,请参见connect.microsoft.com/SQLServer/feedback/details/2178150

#1 楼

表名前缀有很好的理由。

考虑:

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 s的字段。

评论


因此,基本上,这是防止错误的命名约定吗?我认为使用开始事务和提交事务比使用(imo)更令人讨厌的命名方案更好。

–雷切尔(Rachel)
2012年4月17日下午13:51

@Rachel:这是为了1.明确性2.避免不必要的列别名3.允许JOIN..USING 4.惹恼在单个对象中工作的PHP猴子,而不是集合

– gbn
2012-04-17 13:54

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

–安迪
2012年4月17日在16:28

@Andy在运行DELETE之前,我总是做一个SELECT来查找我的记录,一旦运行了语句,我总是在提交之前验证行数是我期望的。

–雷切尔(Rachel)
2012年4月17日在16:49

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

–安迪
2012年4月17日在17:03

#2 楼

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

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

这会导致很多问题。首先,您必须始终记住何时调用列“ id”以及何时调用列“ customer_id”。如果您搞砸了,则会导致第二个问题。如果您有一个带有数十个左右联接的大型查询,并且没有返回任何数据,请尽情享受在哪里玩Waldo并寻找这种错字的乐趣:

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

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

#3 楼

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


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


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


避免不必要的列别名
您可以现在,通过将SELECT CustomerId, ProductIdCustomers而不是Products结合起来的查询编写SELECT Customer.Id as CustomerId, Products.Id as ProductId,可以使用JOIN..USING语法>

在搜索中更容易找到密钥
如果您要在大型解决方案中查找客户的身份字段,则搜索Customer JOIN Products USING (CustomerId)比搜索Customer JOIN Products ON Customer.Id = Products.Id有用得多


如果您能想到此命名约定还有其他优点,请告诉我,我将其添加到列表中。
是否选择使用唯一或相同的列名进行标识字段取决于您,但是无论您选择什么,请保持一致:)

#4 楼

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

在某些情况下,在每个表上粘贴“ ID”并不是最好的主意:USING关键字(如果支持)。例如,如果您有带有列fooTablefooTableId和带有外键barTablefooTableId列,则可以这样构造您的查询:

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)


#5 楼

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

例如,假设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.partname和client.clientname而不是part.name和client.name)。这就是存在INNER JOIN ... USING <key>NATURAL JOIN语法的原因。
但是,如今,随着许多语言中容易使用的ORM层,数据库通常被设计为OO语言的持久层。在不同类中具有相同作用的变量很自然地被称为同一变量(part.name和client.name,而不是part.partname和client.clientname)。在这种情况下,我倾向于将“ ID”用作主键。

#6 楼


我们雇用了一个第三方供应商来实际上为我们创建一些东西
将其所有标识列都命名为Ident,只是为了避免使用Id。


使用“ Ident”代替如果在所有表上都使用了“ Ident”,“ Id”的定义并不能真正解决任何问题。对于这种情况:


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



#7 楼

我将自己的列命名为CustomerID而不是ID,因此,每当键入

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


SQL Prompt会立即建议以下内容

ON c.CustomerID = o.CustomerID 


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

#8 楼

这是为什么您不会将所有varchar字段命名为“ UserText”和“ UserText1”之类的原因,或者是为什么您不会使用“ UserDate”和“ UserDate1”的原因。
通常,如果您在表中有一个标识字段,这是您的主键。如果两个表中的主键都是id,那么如何建立一个带有父表外键的子表?

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

#9 楼

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

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

#10 楼

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