数据库表中ID列的命名

128

我想知道人们对于数据库表中ID列命名的看法。

如果我有一张名为Invoices的表,并且它的主键是一个自增长列,我会将该列命名为InvoiceID,这样我就不会与其他表产生命名冲突,而且很明显它是什么。

在我现在工作的地方,他们全部都将ID列命名为ID。

因此,他们将执行以下操作:

Select  
    i.ID 
,   il.ID 
From
    Invoices i
    Left Join InvoiceLines il
        on i.ID = il.InvoiceID

现在,我看到几个问题:
1. 你需要在选择中对列进行别名
2. ID = InvoiceID 在我的脑海中无法匹配
3. 如果您没有对表进行别名,并引用了InvoiceID,它是否明显属于哪个表格?

其他人对这个话题有什么想法?


http://www.yiiframework.com/wiki/227/guidelines-for-good-schema-design/#hh4 - Handsome Nerd
可能是外键命名方案的重复问题。 - philipxy
24个回答

179

我总是更倾向于使用ID作为id列的名称,然后使用TableName + ID作为外键。这样所有表格都有相同的id字段名称,且没有多余的描述。对我来说,这似乎更简单,因为所有表格都具有相同的主键字段名称。

至于连接表格并不知道哪个Id字段属于哪个表格,在我看来,查询应该编写成处理这种情况。在我们工作的地方,我们总是使用表/表别名来优先显示语句中使用的字段。


Note: html标签已经被保留。

3
我意识到这是一个旧帖子,但我同意。这也使得在数据访问层进行映射更容易:如果所有“对象”都有一个必须唯一的Id字段,那么在数据访问层制作像删除项目这样的方法时,您可以将它们设为通用的,因为您可以收到任何Id的列表,并且知道您只需要从该特定表中删除where Id = blah,而不必保留每个表的唯一映射以及表名称/程序逻辑名称映射。 - Mike
2
和你一样,Kevin。例如:user_id、role_id 用于 PKey,而 role_user_id 用于 FKey。在大型项目中这是很好的做法。因为如果所有 ID 字段都命名为“id”,那就太混乱了。但我认为这是个人喜好,有些人认为只使用“id”更清晰明了。 - Cheung
3
这是我的偏好。仅仅因为跨表查询更难阅读,并不意味着Id列应该被更改以使其更容易理解。只需使您的别名更加描述性即可。 - tmutton
1
我怀疑在实体名称前缀ID的惯例来自于人们出于各种原因使用"select * from"。在一个容忍"select *"的环境中,现实通常会扭曲成支持从任何地方选择所有内容的大规模选择。如果你不盲目地选择所有内容,这没有多少意义。还有关于USING和自然连接的引用,它们非常危险,也不像是我的论点,因为它们有效地阻止你使用基于角色的外键命名约定。 - Roman Polunin
1
感谢上帝,你没有在我的数据库中创建表。 - gotqn

64

最近我们公司有关于这个问题的技术争论。随着LINQ的出现,我认为使用“tablename+ID”这种冗余模式更加明显地显得愚蠢。如果你手写 SQL 时需要指定表名来区分FK,那么只使用ID不仅可以节省打字时间,而且能够使SQL更加清晰,因为你可以清楚地看到哪个是PK,哪个是FK。

例如:

FROM Employees e LEFT JOIN Customers c ON e.ID = c.EmployeeID

这个语句告诉我这两个表是如何连接的,同时也明确了哪个是PK,哪个是FK。而在旧的风格中,你只能希望它们的命名合理或者自己去查找。


3
除非我生命中从未见过一个开发者写你的例子。他们会写:LEFT JOIN Customers as c ON e.ID = c.EmployeeID对我来说,这更清晰:LEFT JOIN Customers as c ON e.EmployeeID = c.EmployeeID而且哪个项目是外键显然由表名决定。显然,customer.employeeId是外键,而employee.employeeId不是。 - dallin
8
许多人(例如编写非常复杂SQL的报告撰写人员和BI专家)仍然需要手写SQL进行导入和导出。数据库设计也必须考虑到他们,而不仅仅是应用程序开发人员。 - HLGEM
2
@dallin 我相信很多开发人员并不会真正考虑将其编写为 e.ID = c.EmployeeID 或者另一种方式 c.EmployeeID = e.ID。我想这只是你习惯于什么。我真的必须思考编写特定顺序的原因(除了在当时教你的人所学到的)。而且,我认为 Employee.ID 比 Employee.EmployeeID 更容易阅读。 - SuperDre

38

ID是SQL反模式。请参见http://www.amazon.com/s/ref=nb_sb_ss_i_1_5?url=search-alias%3Dstripbooks&field-keywords=sql+antipatterns&sprefix=sql+a

如果你有许多表,它们的id列都使用ID作为列名,那么这会使报表生成变得更加困难。这会模糊含义并且使复杂查询变得更加难以阅读,同时还需要在报表中使用别名进行区分。

此外,如果有人愚蠢到在可用的数据库中使用自然连接,那么就会连接到错误的记录。

如果您想使用某些数据库支持的USING语法,则不能使用ID。

如果您在拷贝连接条件时忘记更改联接条件中的别名,那么使用ID将很容易出现错误连接。

因此,您现在有

select t1.field1, t2.field2, t3.field3
from table1 t1 
join table2 t2 on t1.id = t2.table1id
join table3 t3 on t1.id = t3.table2id

当你的意思是

select t1.field1, t2.field2, t3.field3 
from table1 t1 
join table2 t2 on t1.id = t2.table1id
join table3 t3 on t2.id = t3.table2id

如果您使用tablenameID作为id字段,这种偶然的错误发生的可能性要小得多,并且更容易发现。


11
@spencer7593,仅仅因为你喜欢ID并不意味着它不是一种反模式。当你使用表名ID时,在连接方面犯错误会更加困难,因为你会立即得到语法错误提示。 - HLGEM
10
同义的列应该具有相同的名称 - 通过使用表名前缀,您可以在一个表中拥有一个名为table1ID的主键列,在另一个表中拥有一个外键列也称为table1ID - 这样您就知道它们是相同的东西。我在20年前就学过这个做法,它从未让您失望。 - amelvin
14
不!这就是蓝精灵命名规则! - Ross
25
我不喜欢这种惯例,因为它意味着你几乎被迫给所有的表取别名,以便你不会重复命名表格,再次命名表格,然后添加ID。你的理由是可以的,但如果使用ID,表格名称仍然在ID前面。join table2 on table1.id = table2.table1id... 这同样啰嗦冗长,既不冗余,也不强制使用晦涩的别名来避免上述的冗余...而我认为这是SQL开发的大敌。 - Anther
22
如果你的表中有一个 Name 字段,是否应该将其改为 Table1Name,以避免出现同样的���题? 是否应该为相同的原因在所有列名前加上表名? 这听起来似乎不太对。 - Joanvo
显示剩余11条评论

31

我们使用InvoiceID而不是ID。这使得查询更易读 - 当您只看到ID时,它可能表示任何东西,尤其是当您将表别名为i时。


4
使用 Id 更为适当。该列在表 "invoices" 中,因此这应该足够了。如果您编写跨表查询,应使用更具描述性的别名。"i" 不够明确,应该称其为 "invoices"。 - tmutton
2
仅凭“ID”并不清楚它指的是发票。假设您还有到账户和人员的外键,那么哪一个是“ID”?在连接中,读取a.InvoiceID = b.InvoiceID比a.ID = b.InvoiceID更容易,并且可以轻松调试。 - Jason Cohen
6
这句话的意思是表别名有问题,而不是列名。 - Joshua Schlichting
1
但是当给表起别名时,您仍然有表名,因此您知道i.ID来自发票,并且对我来说,i.ID = p.InvoiceID比i.InvoiceID = p.InvoiceID更易读。@JasonCohen使用a.InvoiceID = b.InvoiceID,您仍然必须弄清哪个是外键,哪个不是。通常,如果您要组合表,则会为特定字段添加前缀,因此在您的情况下,它将是a.ID(帐户)或p.ID(产品),否则SQL处理器无论如何都无法区分它。 - SuperDre
1
@NibblyPig,这不是要求必须输入"Invoice"这个单词,而是要在所有地方保持一致和通用性。当它说i.ID时,我知道i是主表;如果它说i.InvoiceID = y.InvoiceID,您也需要查找以确定哪个表可能是主表。是的,SQL的大块是令人头疼的,但如果您不使用别名,我更愿意键入Invoice.id = MyOtherTable.InvoiceID,而不是Invoice.InvoiceID = MyOtherTable.InvoiceID。但是,如果您只有两个表,则甚至可以使用ID = InvoiceID而不使用别名。 - SuperDre
显示剩余2条评论

27

我同意Keven和其他人的观点,即表的主键应该仅为Id,而外键则列出OtherTable + Id。

然而,我希望添加一个最近使这个论点更有份量的理由。

在我目前的职位上,我们正在使用实体框架(Entity Framework)生成POCO。将Id作为主键使用标准命名约定,允许继承一个基本POCO类,用于针对共享一组常见列名的表的验证等操作。而对这些表使用Tablename + Id作为主键,则破坏了使用基类的能力。

这只是一些思考的食粮。


8
在现实世界中,使用通用的命名方式可以提高代码在特定情况下的重用性。这将受到许多开发人员的关注,因为有数百万的.NET开发人员,而且很多人会使用EF。 - codingoutloud
在我的工作中,不仅仅是EF和类似的东西,我们还有很多通用的方法。因此,一个webservice将会有类似于 List<Result> Save<T>(List<int> Ids); 的东西。由于我们知道每个表都有一个Id列作为主键,所以我们可以通过简单的C#对象到表格的映射(类似于 <Customer, "Customers">,<BillingCode, "BillingCodes">(或者更好的存储过程名称))根据传递的对象动态生成SQL语句,从而无需重复编写每种类型对象的保存/删除/编辑方法。 - Mike
在我看来,按照惯例编程是不好的。主键是存在于数据库中的东西,而不仅仅是因为被称为Id的惯例。依赖命名正确是不好的。如果你有一个没有主键或者有复合键的表会发生什么?如果你从另一个部门管理的数据库中提取数据,而他们不使用Id,那会怎样?我会非常谨慎地依赖于名称Id。 - NibblyPig

17
这并不是真的很重要。在所有命名约定中,你可能会遇到类似的问题。
保持一致是很重要的,这样你就不必每次写查询时都查看表定义。

14

我的偏好是将ID作为主键,ForeignKey命名为TableNameID。我还喜欢在大多数表中添加一个“name”列,在其中保存条目的可读标识符(即名称:-) 。这种结构提供了应用程序本身的很大灵活性,我可以以相同的方式批量处理表格。这是一件非常强大的事情。通常,面向对象软件是建立在数据库之上的,但是由于数据库本身不允许,因此无法应用面向对象工具集。仅仅拥有id和name这两列并不是很好,但这是一步。

选择
i.ID , il.ID From Invoices i 左连接 InvoiceLines il on i.ID = il.InvoiceID

我为什么不能这样做?

Select  
    Invoices.ID 
,   InvoiceLines.ID 
From
    Invoices
    Left Join InvoiceLines
        on Invoices.ID = InvoiceLines.InvoiceID

在我看来,这段代码非常易读简洁。通常将变量命名为 i 和 il 是一个很糟糕的选择。


13
我刚开始在一家公司工作,该公司只使用“ID”(在核心表中,由外键TableNameID引用),而且我已经发现了两个直接由此导致的生产问题。其中一个情况是查询使用了“... where ID in (SELECT ID FROM OtherTable ...”,而不是“... where ID in (SELECT TransID FROM OtherTable ...”。有人能诚实地说如果使用全名和一致的名称,错误语句会变成“... where TransID in (SELECT OtherTableID from OtherTable ...”那么这将更容易发现吗?我认为不会。另一个问题出现在重构代码时。如果你使用了一个临时表,而以前的查询是基于核心表的,那么旧代码就会读取“... dbo.MyFunction(t.ID) ...”,如果不改变“t”指向的是一个临时表而不是核心表,你甚至不会得到错误提示——只会得到错误结果。如果生成不必要的错误是目标(也许有些人没有足够的工作?),那么这种命名约定非常棒。否则,一致的命名方式是正确的选择。

4
+1 对于现实世界中更具体名称改善可维护性的真实示例。 - codingoutloud

9
我个人偏向(如上所述)将Table.ID作为PK,并将TableID作为FK。甚至(请不要开枪),Microsoft Access也推荐这样做。
然而,我也知道一些生成工具更喜欢使用TableID作为PK,因为它们倾向于将所有包含'ID'单词的列名链接在一起,包括ID!!! 甚至查询设计器在Microsoft SQL Server中也是如此(每次创建一个查询时,都要在所有表格的列ID上撕掉所有不必要的新关系)。
因此,尽管我的内部强迫症很讨厌它,但我仍然遵循TableID约定。让我们记住,它被称为数据BASE,因为它将成为希望出现的许多应用程序的基础。所有技术都应受益于一个规范化清晰的模式描述。
毋庸置疑,当人们开始使用TableName、TableDescription等内容时,我会有自己的限制。在我看来,惯例应该遵循以下原则:
  • Table name: Pluralized. Ex. Employees
  • Table alias: Full table Name, singularized. Ex.

    SELECT Employee.*, eMail.Address
    FROM Employees AS Employee LEFT JOIN eMails as eMail on Employee.eMailID = eMail.eMailID -- I would sure like it to just have the eMail.ID here.... but oh well
    

[更新]

此外,在此线程中还有一些关于“关系类型”或角色导致重复列的有效帖子。例如,如果一个商店有一个 EmployeeID, 那么这告诉我什么都没有。因此,我有时会做一些类似于 Store.EmployeeID_Manager 的事情。当然,它有点大,但至少人们不会疯狂地尝试找到 表ManagerID 或者 EmployeeID 在那里做什么。在查询时,我会简化为:SELECT EmployeeID_Manager as ManagerID FROM Store。


1
我认为你的观点对于数据库的美观和教学目的是很好的,但就功能而言,我认为这是一个问题。复数表名会促进表之间的不一致性,PK Id <-> FK IdTable 的名称差异会导致相同事物的名称不同。同时,在编程中输入类似 User.UserId 这样的内容有点奇怪。 - Machado

7

为了简单起见,大多数人将表格上的列命名为ID。如果它在另一个表格上有外键引用,则他们明确地称其为InvoiceID(以您的示例为例)。在连接的情况下,您无论如何都要为表格取别名,因此显式的inv.ID仍然比inv.InvoiceID更简单。


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