112

我想知道人们对数据库表中 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,那么很明显是什么表它开着?

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

4

24 回答 24

169

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

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

于 2008-10-16T13:46:15.770 回答
57

最近在我的公司里就这件事发生了一场书呆子之争。LINQ 的出现让多余的tablename+ID模式在我看来更加愚蠢。我认为最有理智的人会说,如果您以必须指定表名来区分FK的方式手写 SQL,那么这不仅可以节省键入时间,而且可以使您的 SQL 更清晰地使用里面的ID,你可以清楚的看到哪个是PK,哪个是FK

例如

FROM 员工 e LEFT JOIN 客户 c ON e.ID = c.EmployeeID

不仅告诉我这两者是联系在一起的,而且告诉我哪个是PK哪个是FK。而在旧样式中,您不得不查看或希望它们被命名好。

于 2008-10-16T15:30:08.073 回答
33

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 的表,那么您的报告就会变得更加困难。它模糊了含义并使复杂的查询更难阅读,并且要求您使用别名来区分报告本身。

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

如果您想使用某些 dbs 允许的 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 字段,这种意外错误发生的可能性要小得多,而且更容易找到。

于 2011-09-21T17:41:38.367 回答
31

我们使用InvoiceID,不是ID。它使查询更具可读性——当您ID单独看到时,它可能意味着任何事情,尤其是当您将表别名为i.

于 2008-10-16T13:36:37.600 回答
26

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

然而,我想补充一个最近更加重视这一论点的原因。

在我目前的职位上,我们正在使用使用 POCO 生成的实体框架。使用 Id 的标准命名约定,PK 允许继承具有验证的基础 poco 类,例如共享一组公共列名的表。使用 Tablename + Id 作为每个表的 PK 会破坏为这些表使用基类的能力。

只是一些思考的食物。

于 2012-06-07T14:34:32.087 回答
14

我的偏好也是主键的 ID 和外键的 TableNameID。我还喜欢在我保存条目的用户可读标识符(即名称:-))的大多数表中都有一个“名称”列。这种结构为应用程序本身提供了极大的灵活性,我可以以同样的方式处理大量表格。这是一个非常强大的东西。通常一个 OO 软件是建立在数据库之上的,但是 OO 工具集不能应用,因为 db 本身不允许。拥有列 id 和 name 仍然不是很好,但这是一个步骤。

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

为什么我不能这样做?

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

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

于 2011-09-21T15:34:19.807 回答
13

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

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

于 2008-10-16T13:40:13.177 回答
10

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

在一种情况下,查询使用“... where ID in (SELECT ID FROM OtherTable ...”而不是“... where ID in (SELECT TransID FROM OtherTable ...”。

任何人都可以诚实地说,如果使用完整、一致的名称,而错误的语句会显示为“......其中 TransID in (SELECT OtherTableID from OtherTable ...”,那将不会更容易发现?我不认为所以。

重构代码时会出现另一个问题。如果您使用临时表,而之前查询离开核心表,则旧代码读取“... dbo.MyFunction(t.ID) ...”,如果没有更改,但“t”现在指的是临时表而不是核心表,您甚至不会收到错误 - 只是错误的结果。

如果生成不必要的错误是一个目标(也许有些人没有足够的工作?),那么这种命名约定很棒。否则一致的命名是要走的路。

于 2010-11-16T22:00:31.630 回答
8

个人更喜欢(如上所述)PKTable.IDFKTableID。甚至(请不要向我开枪)Microsoft Access 也建议这样做。

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

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

因此,尽管我的内部强迫症很讨厌它,但我还是遵循 TableID约定。让我们记住它被称为 Data BASE,因为它将成为许多许多应用程序的基础。并且所有技术都应该受益于具有清晰描述的良好规范化模式。

不用说,当人们开始使用 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
    

[更新]

此外,由于“关系类型”或角色,此线程中有一些关于重复列的有效帖子。例如,如果一个 Store 有一个EmployeeID,这告诉我蹲下。所以我有时会做一些类似Store.EmployeeID_Manager的事情。当然它有点大,但至少人们不会疯狂地试图找到表 ManagerIDEmployeeID在那里做什么。当查询是 WHERE 时,我将其简化为: SELECT EmployeeID_Manager as ManagerID FROM Store

于 2013-08-14T21:45:10.820 回答
7

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

于 2008-10-16T13:38:37.953 回答
4

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

显然,命名约定是主观的并且是风格问题。我发现 ISO/IEC 11179 指南是一个有用的起点。

对于 DBMS,我将表视为实体的集合(除了那些只包含一行的表,例如 cofig 表、常量表等),例如 myemployee_ID是键的表将被命名为Personnel. 所以马上TableNameID公约对我不起作用。

我已经看到TableName.ID=PK TableNameID=FK用于大型数据模型的样式,不得不说我觉得它有点令人困惑:我更喜欢标识符的名称始终相同,即不会根据它恰好出现在哪个表中更改名称。需要注意的是上述风格似乎在商店中使用,它们向每个IDENTITY表添加(自动增量)列,同时避开外键中的自然键和复合键。这些商店往往没有正式的数据字典,也没有从数据模型构建。同样,这只是一个风格问题,我个人并不赞同。所以最终,它不适合我。

综上所述,当表名提供了这样做的上下文时,我可以看到有时从列名中删除限定符的情况,例如,命名的元素employee_last_name可能会简单地last_name出现在Personnel表中。这里的基本原理是域是“人们的姓氏”,并且更有可能UNION与其他表中的last_name列一起使用而不是用作另一个表中的外键但话又说回来......我可能只是改变主意,有时你永远无法分辨。就是这样:数据建模既是艺术,又是科学。

于 2008-10-16T19:32:30.220 回答
2

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

我所说的第一条语句的意思是,你可以使用其他东西来代替 ID,比如“recno”。那么这个表就会有一个 invoice_recno 的 PK 等等。

干杯,本

于 2008-10-16T15:12:47.513 回答
2

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

 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

当然,它比其他一些例子要长。但微笑。这是为了后代,有一天,一些可怜的初级程序员将不得不改变你的杰作。在此示例中,没有歧义,并且随着向查询中添加了其他表,您会很感激这里的冗长。

于 2008-10-16T15:32:53.167 回答
2

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

  • 小写数据库字段名称
  • 大写表名
  • 使用下划线分隔字段名称中的单词 - 在代码中将它们转换为 Pascal 大小写。
  • pk_前缀表示主键
  • _idsuffix 表示一个整数,自增 ID
  • fk_前缀表示外键(不需要后缀)
  • _VW视图的后缀
  • is_布尔值前缀

因此,一个名为 NAMES 的表可能具有字段pk_name_id, first_name, last_name, is_alive,fk_company一个名为 的视图LIVING_CUSTOMERS_VW,定义如下:

选择名字,姓氏
来自联系人姓名
WHERE (is_alive = 'True')

不过,正如其他人所说,只要它是一致的并且不会不必要地混淆您的含义,几乎任何方案都可以工作。

于 2008-10-17T19:16:40.720 回答
1

我确实讨厌简单的 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 

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

于 2008-10-16T13:38:21.557 回答
1

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

于 2008-10-16T13:39:41.477 回答
1

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

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

如果该列不用于外键,仅用于唯一标识行以进行编辑编辑或删除,我将其命名为“PK”。

于 2008-10-16T13:43:38.147 回答
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 足够冗长,但不会使其更冗长。

于 2008-10-16T14:33:19.043 回答
1

我为自己保持一致(其中表具有用作 ID 的单列主键)所做的事情是命名表的主键Table_pk。任何我有一个指向该表主键的外键的地方,我都会调用 column PrimaryKeyTable_fk。这样我就知道如果我的Customer_pkCustomer 表中有 a 和Order 表中有 a Customer_fk,我知道 Order 表是指 Customer 表中的一个条目。

对我来说,这很有意义,尤其是对于我认为它更容易阅读的连接。

SELECT * 
FROM Customer AS c
    INNER JOIN Order AS c ON c.Customer_pk = o.Customer_fk
于 2008-10-17T18:49:47.700 回答
0

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

DomainName 通常但不总是与 TableName 相同。

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

DomainName & ID 可用于命名外键和主键。当外键以它们引用的列命名时,这可能有助于助记符。形式上,不需要将外键的名称与它引用的键绑定,因为引用完整性约束将建立引用。但是在读取查询和更新时它非常方便。

偶尔,域名 || 不能使用“ID”,因为同一个表中会有两列同名。示例:Employees.EmployeeID 和Employees.SupervisorID。在这些情况下,我使用 RoleName || 'ID',如示例中所示。

最后但同样重要的是,我尽可能使用自然键而不是合成键。在某些情况下自然密钥不可用或不可信,但在很多情况下自然密钥是正确的选择。在这些情况下,我让自然键取它自然具有的名称。这个名字通常甚至没有字母“ID”。示例:OrderNo,其中 No 是“Number”的缩写。

于 2008-10-17T10:57:05.627 回答
0

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

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

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

我在 SQL 和我使用的所有语言(主要是 C#、Javascript、VB6)中保持相同的名称。

于 2008-10-17T13:58:53.747 回答
0

有关经过深思熟虑的命名表和列系统,请参阅 Interakt 站点的命名约定。该方法为每个表(_prd产品表或_ctg类别表)使用后缀,并将其附加到给定表中的每一列。因此 products 表的标识列将是id_prd并且因此在数据库中是唯一的。

他们更进一步帮助理解外键:产品表中引用类别表的外键将idctg_prd很明显它属于哪个表(_prd后缀)和它引用的表(类别) .

优点是不同表中的标识列没有歧义,并且您可以通过列名一眼看出查询引用了哪些列。

于 2008-10-17T14:36:52.080 回答
0

已经有很多答案了,但我想添加两个我在上面没有看到的主要内容:

  • 客户向您寻求支持。

很多时候,客户或用户甚至另一个部门的开发人员遇到了障碍,并联系我们说他们在进行操作时遇到了问题。我们问他们有什么问题。现在,他们在屏幕上看到的数据,例如带有客户姓名、订单数量、目的地等的网格是许多表格的集合。他们说他们在使用 id 83 时遇到了问题。没有办法知道那是什么 id,它是哪个表,如果它只是被称为“id”。

即,一行数据没有给出任何指示它来自哪个表。除非您碰巧很了解数据库的架构,在复杂系统或您被告知接管的非新建系统中很少出现这种情况,否则即使您有更多数据,您也不知道 id=83 意味着什么例如姓名、地址等(甚至可能不在同一张表中!)。

此 id 可能来自网格,也可能来自 API 中的错误,或者将错误消息转储到屏幕或日志文件的错误查询。

通常,开发人员只是将“ID”转储到列中而忘记了它,并且数据库通常有许多类似的表,例如 Invoice、InvoiceGrouping、InvoicePlan,并且 ID 可能适用于其中的任何一个。沮丧的是,您查看代码以查看它是哪一个,并看到他们在模型上也将其称为 Id,因此您必须深入研究该页面的模型是如何构建的。我无法计算我必须这样做多少次才能弄清楚 Id 是什么。很多。有时您还必须挖掘一个仅返回“Id”作为标题的 SPROC。恶梦。

  • 当清楚出了什么问题时,日志文件会更容易

SQL 通常会给出非常糟糕的错误消息。“无法插入 ID 为 83 的项目,列将被截断”或类似的东西很难调试。通常错误消息不是很有帮助,但通常错误消息会通过仅转储主键名称和值来模糊地尝试告诉您哪些记录被破坏了。如果它是“ID”,那么它根本没有帮助。

这只是我觉得其他答案中没有提到的两件事。

I also think that a lot of comments are 'if you program in X way then this isn't an issue', and I think the points above (and other points on this question) are valid specifically because of the way people program and because they don't have the time, energy, budget and foresight to program in perfect logging and error handling or change engrained habits of quick SQL and code writing.

于 2021-08-17T10:34:46.190 回答
-1

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

  1. 为表名使用短(3-4 个字符)昵称,即 Invoice - inv、 InvoiceLines -invl
  2. 使用这些昵称命名表中的列,即inv_idinvl_id
  3. 对于参考列,请使用invl_inv_id名称。

这样你就可以说

SELECT * FROM Invoice LEFT JOIN InvoiceLines ON inv_id = invl_inv_id
于 2008-10-16T13:39:46.640 回答