数据库表中ID列的命名


97

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

如果我有一个名为Invoices的表,且其标识列的主键为该表,则应将该列称为InvoiceID,这样我就不会与其他表发生冲突,这很明显。

在我目前工作的地方,他们已经将所有ID列称为ID。

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

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

现在,我在这里看到一些问题:
1.您将需要对select的列进行别名
2. ID = InvoiceID不适合我的大脑
3.如果您未对表进行别名并引用InvoiceID,那么显然是哪个表它开着?

其他人对此话题有何看法?


1
哎呀,我们周围都是品味不好的人!;-)
Vinko Vrsalovic


2
如何选择第二个答案作为“ 答案 ”?
显示名

可能重复使用外键命名方案
philipxy

Answers:


23

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字段,则这种意外错误的发生率将大大降低,并且更容易发现。


8
@ spencer7593,只是因为您喜欢ID并不意味着它不是反模式。当您拥有表名ID时,很难在连接中犯错误,因为您会立即收到语法错误。
HLGEM 2012年

6
+1的同义列应具有相同的名称-通过使用表名前缀,您可以在名为table1ID的主键列和在另一个名为table1ID的表中拥有外键列-并且您知道它们是同一对象。我在20年前就曾受过这样的教育,并且这种实践不会让您失望。
amelvin

9
没有!这是蓝精灵的命名约定!
罗斯

19
我不喜欢该约定,因为它实际上意味着您实际上被迫为所有表加上别名,以免您不必要地重复命名该表,再次命名该表并添加ID。 join table2 on table1.table1id = table2.table1id。您的推理是可以的,除了如果您使用id之外,表的名称始终在ID的前面。join table2 on table1.id = table2.table1id...只是冗长而不冗余,也不强迫别名模糊以防止刚才提到的冗余..我认为这是sql开发的祸根。
花药

13
然后,如果Name表中有一个字段,是否应该更改它Table1Name以避免该字段出现相同的问题?是否应该出于相同的原因在所有列的前面加上表名?听起来不对。
Joanvo

151

我总是更喜欢ID而不是ID列的TableName + ID,然后是外键的TableName + ID。这样,所有表的id字段都具有相同的名称,并且没有多余的描述。对我来说,这似乎更简单,因为所有表都具有相同的主键字段名称。

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


2
我意识到有一个老话题,但我同意。这也使在数据访问层的映射更加容易:如果所有“对象”都有一个ID字段,该ID字段必须是唯一的,那么当在数据访问层使用方法来执行删除项之类的操作时,可以将它们设为通用,因为您可以接收到任何东西的ID都知道,您只需要删除该特定表中的Id = blah,而不必保留每个表的唯一映射以及表名/程序逻辑名映射。
Mike

1
凯文,和你一样。例如:user_id,用于PKey的role_id和用于FKey的role_user_id。在大型项目上很好。因为如果将所有ID字段都命名为“ id”,那么就太混乱了。但是我认为这是个人喜好,有人认为仅使用“ id”是明确的。

2
这是我的偏爱。仅仅因为跨表查询更难以阅读并不意味着应更改Id列以使其更容易。只是使您的别名更具描述性即可。
tmutton'3

1
我怀疑以实体名称作为ID前缀的惯例是出于某种原因而使用“ select * from”的人。在容忍“选择*”的环境中,现实通常会失真,无法支持从任何地方进行批量选择。如果您不盲目选择所有内容,则没有任何意义。也有一些关于USING和自然联接的引用,它们非常危险,而且根本不像我所说的那样,因为它们有效地阻止了您使用基于角色的外键命名约定。
罗曼·波伦宁

53

最近在我的公司里,关于这件事的争论很激烈。LINQ的出现使冗余表名+ ID模式在我眼中显得更加愚蠢。我认为大多数理性的人都会说,如果您以某种方式来编写SQL,即必须指定表名来区分FK,那么不仅节省了键入时间,而且还使SQL的使用更加清晰明了。通过ID,您可以清楚地看到哪个是PK,哪个是FK

例如

来自员工e左加入客户c ON e.ID = c.EmployeeID

不仅告诉我两者是链接的,而且还告诉我PKFK。而在旧样式中,您不得不外观或希望它们被命名为好。


2
除了我一生中从未认识过一个开发人员来编写您的示例。他们改写为:LEFT JOIN客户为c ON e.ID = c.EmployeeID对我来说,这更清楚了:LEFT JOIN客户为c ON e.EmployeeID = c.EmployeeID哪个项目是外键,通过表名是显而易见的。显然,customer.employeeId是外键,而employee.employeeId不是。
达林

7
很多人(例如编写非常复杂的sql的报表编写器)和执行进出口的BI专家仍然需要手写SQl。数据库设计也必须适应它们,而不仅仅是应用程序开发人员。
HLGEM 2013年

29

我们使用InvoiceID而不是ID。它使查询更具可读性- ID仅当您看到它时,它可能具有任何意义,尤其是当您将表别名为时i


同意,您指出了不使用“ id”的主要原因,因为在SQL或LINQ上,我们总是使用表别名,例如inoice的表名是“ i”,SQL或LINQ的表名是“ product”的表等。–
Cheung

2
使用ID更合适。该列在“发票”表中,这样就足够了。如果要编写跨表查询,则应使用更具描述性的别名。“ i”是不够的。称其为“发票”。
2015年

1
尚不清楚“ ID”仅表示发票。假设您还具有Accounts and People的外键。现在,哪个是“ ID”?比方说,在联接中读取a.InvoiceID = b.InvoiceID而不是a.ID = b.InvoiceID并不是一件容易的事,并且无法轻松调试它。
杰森·科恩

22

我同意凯文(Keven)和其他一些人的观点,表的PK应该只是ID,外键列出OtherTable + Id。

但是,我想补充一个理由,该理由最近更加重视了这一论点。

在我目前的职位上,我们正在使用通过POCO生成的实体框架。使用Id的标准命名约定,PK允许通过验证继承基poco类,并且对于共享一组公共列名的表也是如此。将这些表中的每个表都使用Tablename + Id作为PK会破坏对这些表使用基类的能力。

只是需要一些思考。


8
+1是有关在特定情况下通用命名如何提高代码重用性的现实示例(由于有数百万的.NET开发人员,很多开发人员都将关心它,因此许多开发人员都会使用EF)。
codingoutloud

在我的工作中,不仅是EF之类的东西,我们还有许多通用方法。因此,Web服务将具有类似List <Result> Save <T>(List <int> Ids)的内容;因为我们知道每个表都有一个ID列作为主键,所以我们可以通过将C#对象简单映射到它们的表(例如<Customer,“ Customers”>,<BillingCode,“ BillingCodes”>(或者更好的存储过程的名称),基于传递瞧不重复保存/每类对象的删除/编辑方法的对象上的飞SQL。
迈克·

11

我的首选项也是主键的ID和外键的TableNameID。我还喜欢在大多数表中都有一个“名称”列,其中保存着该条目的用户可读标识符(即名称:-)。这种结构为应用程序本身提供了极大的灵活性,我可以用相同的方式处理大量表。这是非常强大的事情。通常,OO软件是基于数据库构建的,但是OO工具集无法应用,因为db本身不允许这样做。具有列ID和名称仍然不是很好,但这是一个步骤。


从发票i中选择i.ID,il.ID。i.ID = il.InvoiceID上的左联接发票行il

为什么我不能这样做?

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

我认为这是非常易读和简单的。通常,将变量命名为i和il是一个糟糕的选择。


10

这并不是很重要,您可能会在所有命名约定中遇到较小的问题。

但是保持一致很重要,这样您不必在每次编写查询时都查看表定义。


8

我刚刚开始在仅使用“ ID”的位置(在核心表中,由外键中的TableNameID引用)工作,并且已经发现了两个直接由它引起的生产问题。

在一种情况下,查询使用“ ...在(SELECT ID从其他表中选择ID ...”中的ID,而不是“ ...在(SELECT TransID来自其他表中ID中的ID)”。

任何人都可以诚实地说,如果使用完整,一致的名称,在错误的语句显示为“ ... where TransID in(SELECT OtherTableID from OtherTable ...”中的位置),发现这一点不会容易得多吗?所以。

重构代码时会发生另一个问题。如果您使用临时表,而以前查询不在核心表中,那么旧代码将读取“ ... dbo.MyFunction(t.ID)...”,如果未更改,但“ t”现在表示一个临时表而不是核心表,您甚至都不会得到错误-只是错误的结果。

如果产生不必要的错误是目标(也许有些人工作量不足?),那么这种命名约定就很棒。否则,要保持一致的命名方式。


3
+1是更具体的名称可以改善可维护性的真实示例。
codingoutloud

6

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


6

个人比较喜欢(因为已经如上所述)的Table.IDPK表格IDFK。甚至(请不要射击我)Microsoft Access都建议这样做。

但是,我也知道一些生成工具偏爱PK的TableID的事实,因为它们倾向于链接单词中包含“ ID”的所有列名,包括ID!

甚至查询设计器也在Microsoft SQL Server上执行此操作(对于您创建的每个查询,您最终都会在列ID的所有表上剥夺所有不必要的新创建的关系)

尽管我的内部OCD讨厌它,但我还是遵循TableID约定。让我们记住,这就是所谓的数据基础,因为这将是希望很多很多很多的应用程序来的基础。并且所有技术都应该受益于规范化,描述清晰的架构。

不用说,当人们开始使用TableName,TableDescription等时,我确实会划清界限。我认为,约定应执行以下操作:

  • 表名:复数。例如 雇员
  • 表别名:全表名,单数。例如

    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的事情。当然,它会更大一些,但是,人们至少会发现表ManagerIDEmployeeID在做什么的想法不会发疯。当查询在哪里时,我将其简化为:SELECT EmployeeID_Manager作为ManagerID FROM Store


我认为您的观点对数据库的美观和教学目的有利,但对功能而言,我认为这是一个问题。多个表名会导致表之间的不一致,并且PK Id <-> FK IdTable会在同一事物的名称上产生差异。同时,User.UserId在编程中键入这样的内容有点奇怪。
马查多

4

从正式数据字典的角度来看,我将命名数据元素invoice_ID。通常,数据元素名称在数据字典中将是唯一的,并且理想情况下在整个名称中应具有相同的名称,尽管有时可能会根据上下文要求附加限定词,例如,命名的数据元素employee_ID可在组织结构图中使用两次,因此限定为supervisor_employee_IDsubordinate_employee_ID分别。

显然,命名约定是主观的并且是样式问题。我发现ISO / IEC 11179准则是一个有用的起点。

对于DBMS,我将表视为实体的集合(除了仅包含一行的实体,例如cofig表,常量表等),例如,employee_ID将以my 为键的表命名为Personnel。因此,TableNameID常规对我不起作用。

我已经看过TableName.ID=PK TableNameID=FK大型数据模型上使用的样式,不得不说我觉得有点困惑:我更喜欢标识符的名称在整个过程中都是相同的,即不会根据碰巧出现在哪个表上而更改名称。在商店中使用上述样式似乎是在商店中使用的,它们IDENTITY每张表中增加了一个(自动递增)列,同时避免了外键中的自然键和复合键。这些商店往往没有正式的数据字典,也没有从数据模型构建的倾向。再说一次,这只是一个风格问题,我个人不赞成。所以最终,这不适合我。

综上所述,当表的名称提供了这样做的上下文时,例如当命名的元素employee_last_name可能last_namePersonnel表中变得简单时,我可以看到有时从列名中删除限定符的情况。这里的理由是,域名是“人的姓氏”,更可能被UNION埃德last_name其他表,而不是作为一个外键另一个表,但随后又......我可能只是改变了主意,有时你永远无法分辨。就是这样:数据建模既是艺术,又是科学。


2

我认为只要您保持一致,就可以为“ ID”使用任何内容。包括表名很重要。我建议使用诸如Erwin之类的建模工具来强制执行命名约定和标准,因此在编写查询时,很容易理解表之间可能存在的关系。

我所说的第一句话是,您可以使用诸如“ recno”之类的东西代替ID。因此,此表将具有invoice_recno的PK,依此类推。

干杯本


2

我的投票是针对表ID的InvoiceID。当用作外键时,我也使用相同的命名约定,并在查询中使用智能别名。

 Select Invoice.InvoiceID, Lines.InvoiceLine, Customer.OrgName
 From Invoices Invoice
 Join InvoiceLines Lines on Lines.InvoiceID = Invoice.InvoiceID
 Join Customers Customer on Customer.CustomerID = Invoice.CustomerID

当然,它比其他示例更长。但是,微笑。这是为了后代,有朝一日,一些可怜的初级编码员将不得不改变您的杰作。在此示例中,不存在歧义,并且随着向查询中添加其他表,您将非常感谢您的冗长。


1

对于数据库中的列名,我将使用“ InvoiceID”。

如果我通过LINQ将字段复制到一个未命名的结构中,那么如果它是结构中的唯一ID,则可以在其中将其命名为“ ID”。

如果该列不打算用在外键中,那么它仅用于唯一地标识要编辑或删除的行,则将其命名为“ PK”。


1

如果为每个键指定一个唯一的名称,例如“ invoices.invoice_id”而不是“ invoices.id”,则可以使用“自然联接”和“使用”运算符而不必担心。例如

SELECT * FROM invoices NATURAL JOIN invoice_lines
SELECT * FROM invoices JOIN invoice_lines USING (invoice_id)

代替

SELECT * from invoices JOIN invoice_lines
    ON invoices.id = invoice_lines.invoice_id

SQL足够冗长而又不那么冗长。


您知道SQL Server是否支持自然联接吗?
Arry

我不认为这样。根据connect.microsoft.com/SQLServer/feedback/…的说法,该语法似乎计划在SQL Server 2005之后的某些版本中添加。我知道它可以在PostgreSQL和Oracle中使用。
史蒂芬·休伊格

7
从不,从不,从不使用自然联接。如果在编写查询时一个表具有“描述”字段,则可以。如果以后有人在另一个表中添加一个描述字段,则您也将开始加入该描述字段并完全中断。

1
呵呵,听起来像是现实生活中的声音:)
dland

我只会将自然联接用于临时查询。
史蒂芬·休伊格

1

为了使自己保持一致(在表中有一个单列主键用作ID),我要做的就是命名表的主键Table_pk。在任何有指向该表主键的外键的地方,都称为column PrimaryKeyTable_fk。这样,我知道如果Customer_pk我的客户表和Customer_fk订单表中都有一个,则我知道该订单表引用了客户表中的一个条目。

对我而言,这尤其适用于我认为更容易理解的联接。

SELECT * 
FROM Customer AS c
    INNER JOIN Order AS c ON c.Customer_pk = o.Customer_fk

1

FWIW,我们的新标准(随着每个新项目的变化,呃,我的意思是“演变”)是:

  • 小写数据库字段名称
  • 大写表格名称
  • 使用下划线分隔字段名称中的单词-在代码中将其转换为Pascal大小写。
  • pk_ 前缀表示主键
  • _id 后缀表示一个整数,自动递增的ID
  • fk_ 前缀表示外键(无需后缀)
  • _VW 查看后缀
  • is_ 布尔值的前缀

因此,一台名为名称可能有田pk_name_id, first_name, last_name, is_alive,fk_company以及一个名为视图LIVING_CUSTOMERS_VW,等被定义:

选择名字,姓氏
来自CONTACT.NAMES
在哪里(is_alive ='True')

但是,正如其他人所说,只要方案是一致的,并且不会不必要地混淆您的意思,那么几乎任何方案都将起作用。


0

出于您给出的确切原因,我绝对同意在ID字段名称中包含表名称。通常,这是我要包含表名称的唯一字段。


0

我讨厌简单的ID名称。我强烈希望始终使用invoice_id或其变体。我总是知道哪个表是ID的权威表,但这使我感到困惑

SELECT * from Invoice inv, InvoiceLine inv_l where 
inv_l.InvoiceID = inv.ID 
SELECT * from Invoice inv, InvoiceLine inv_l where 
inv_l.ID = inv.InvoiceLineID 
SELECT * from Invoice inv, InvoiceLine inv_l where 
inv_l.ID = inv.InvoiceID 
SELECT * from Invoice inv, InvoiceLine inv_l where 
inv_l.InvoiceLineID = inv.ID 

最糟糕的是您提到的混合,完全令人困惑。我不得不使用一个数据库,除了最常用的ID之一外,几乎几乎都是foo_id。那简直是地狱。


1
在这篇文章中,我已经多次阅读“发票”一词。现在看起来很有趣
凯文(Kevin)

2
implicit,隐式加入!我想睁大眼睛看着它。
HLGEM 2012年

0

我更喜欢域名|| 'ID'。(即域名+ ID)

DomainName通常但并非总是与TableName相同。

ID本身的问题在于它不会向上扩展。一旦有了大约200个表,每个表都有第一列名为ID的表,数据就开始看起来完全一样。如果您总是用表名来限定ID,那会有所帮助,但没有太大帮助。

DomainName和ID可用于命名外键和主键。当foriegn键在它们所引用的列之后命名时,这可能有助于记忆。正式地,没有必要将外键的名称与其引用的键绑定在一起,因为引用完整性约束将建立引用。但是,在阅读查询和更新时非常方便。

有时,域名|| 不能使用“ ID”,因为同一表中会有两列具有相同的名称。示例:Employees.EmployeeID和Employees.SupervisorID。在那种情况下,我使用RoleName ||。如示例中的“ ID”。

最后但并非最不重要的一点是,我尽可能使用自然键而不是合成键。在某些情况下自然键不可用或不可信,但是在很多情况下自然键是正确的选择。在这种情况下,我让自然键采用自然拥有的名称。此名称通常甚至都没有字母“ ID”。示例:OrderNo,其中No是“ Number”的缩写。


0

对于每个表,我选择一个树字母速记(例如,Employees => Emp)

这样,数字自动编号主键将成为nkEmp

它很短,在整个数据库中都是唯一的,我一眼就知道它的属性。

我在SQL和我使用的所有语言(大多数是C#,Javascript,VB6)中保持相同的名称。


0

请参阅Interakt网站的命名约定,以获取周到的命名表和列的系统。该方法为每个表(_prd产品表或_ctg类别表)使用后缀,并将后缀附加到给定表的每个列中。因此,产品表的标识列将是id_prd并且因此在数据库中是唯一的。

他们进一步走了一步,以帮助理解外键:在产品表中引用类别表的外键应该是idctg_prd这样,这样就很明显它属于哪个表(_prd后缀)以及它所引用的表(类别)。 。

优点是,不同表中的标识列之间没有歧义,并且您可以一眼就知道查询是通过列名引用的列。



-2

您可以使用以下命名约定。它有缺陷,但可以解决您的特定问题。

  1. 使用简短的(3-4个字符)昵称作为表格名称,即invInvoice-,InvoiceLines-invl
  2. 使用这些绰号,即在命名表中的列inv_idinvl_id
  3. 对于参考列,请使用invl_inv_id名称。

这样你可以说

SELECT * FROM Invoice LEFT JOIN InvoiceLines ON inv_id = invl_inv_id

5
ick!我投票反对对表(或任何其他对象)使用短昵称。使用昵称,您将永远无法确切知道简称。请记住,有很多方法可以将其拼写错误;只有一种正确的拼写方法。
James Curran

1
詹姆斯,我不同意。如果您使用的短名称没有描述性,并且您不记得它的含义,那么您选择的名称是错误的,或者您不理解其他人选择的命名约定。
kemiller2002

2
使用别名可以达到相同的效果。选择*从发票inv左加入inv.ID = invl.InvoiceID上的InvoiceLines invl
yfeldblum

2
不不不不。在查询中给表加上别名以使其简短化。但是表名应该是完整的。
网格

2
为什么这么多的程序员懒惰,似乎所有事情的答案都是尽可能少地键入,只是因为很难键入更多内容。
mP。
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.