数据库命名标准介绍表名列名总结

2025-05-26

数据库命名标准

介绍

表名称

列名

概括

介绍

尽管 SQL 已经存在了几十年,但数据库命名约定仍然是临时性的。在从一个项目转到另一个项目时,你经常需要了解它们在数据库设计和命名方面的独特之处。这是因为大多数开发人员并不了解数据库。事实上,在我招聘开发人员的这些年里,即使是那些拥有强大 SQL 技能的开发人员,也很少遇到能够正确规范化数据库的开发人员。

对于许多开发人员来说,数据库是事后才想到的。它只是一个保存数据的地方,直到重要的(*咳嗽*)工作完成。

但本文并非讨论数据库规范化。如果你想了解更多,这里有一个我做的简短演讲,讲解了基础知识。

相反,当您拥有一个可运行的数据库时,我们想知道的问题是“我们可以应用哪些标准来使该数据库更容易使用?” 如果这些标准被广泛采用,数据库将更容易使用,因为您不必在每次使用新数据库时学习和记住一套新的标准。

表名称

CamelCase对比underscore_name

让我们快速解决这个问题。我经常在网上看到数据库示例,其中我们会看到类似CustomerOrders或 的表名customer_orders。您应该使用哪个?您可能希望使用现有的任何标准,但如果您正在创建新的数据库,我建议您using_undercores出于可访问性方面的考虑。“under value”和“undervalue”的含义不同,但前者带有下划线,始终为under_value,而后者为undervalue。使用驼峰命名法时, 和Undervalue相同UnderValue,因为 SQL 不区分大小写。因此,下划线有助于减少歧义。

下划线也能提升可访问性。如果你有视力问题,需要经常调整字体和字号来区分单词,下划线会容易阅读。而对于母语非英语的人来说,驼峰式拼写法(CamelCase)的阅读难度更大。

话虽如此,这只是个人偏好,并非强烈推荐。很多人喜欢驼峰命名法 (CamelCase),这很快就会引发“制表符 vs. 空格”的争论(例如,浪费时间)。

复数表还是单数表?

customer长期以来,数据库理论专家们就数据库表应该使用单数( )还是复数( )存在着激烈的争论customers。我先不深入理论,用务实的态度来解决这个问题:复数表名与保留关键字冲突的可能性更小。

你有用户吗?SQL 有一个user保留字。你想要一个约束表吗?constraint是一个保留字。是audit一个保留字,但你想要一个audit表?只需使用名词的复数形式,大多数保留字就不会在编写 SQL 时给你带来麻烦。我甚至曾经在 PostgreSQL 上遇到过问题,尽管它有一个非常优秀的 SQL 解析器,但当我有一个user表时,ORM 会自动将其别名为user(是的,相同的名称),PostgreSQL 就搞混了。

只要使用复数名称,发生冲突的可能性就会大大降低。

列名

不要命名主键列id

这是我多年来一直犯的一个错误。在与巴黎的一位客户合作时,一位 DBA 抱怨我将 id 列命名为id。我,一个普通的开发人员,认为我们的 DBA(他是一位非常优秀的 DBA)只是在敷衍了事。毕竟,该customers.id列本身没有歧义,但却customers.customer_id重复了信息。

后来我必须从Tau Station MMORPG 中调试以下内容:

SELECT thread.*
  FROM email thread
  JOIN email selected      ON selected.id    = thread.id
  JOIN character recipient ON recipient.id   = thread.recipient_id
  JOIN station_area sa     ON sa.id          = recipient.id
  JOIN station st          ON st.id          = sa.id
  JOIN star origin         ON origin.id      = thread.id
  JOIN star destination    ON destination.id = st.id
LEFT JOIN route
       ON ( route.from_id = origin.id
            AND 
            route.to_id = destination.id )
 WHERE selected.id                = ?
   AND ( thread.sender_id         = ? 
         OR ( thread.recipient_id = ?
              AND ( origin.id = destination.id
                    OR ( route.distance IS NOT NULL
                         AND
                         now() >= thread.datesent
                         + ( route.distance * interval '30 seconds' )
        ))))
ORDER BY datesent ASC, thread.parent_id ASC
Enter fullscreen mode Exit fullscreen mode

(顺便说一句,是的,我确实非常小心我的 SQL 格式)

你看出问题了吗?如果 SQL 语句使用了完整的 ID 名称,例如email_idstar_id和,那么在我输入这条 SQL 语句时station_id,这些错误就会非常明显,而不是等到我试图弄清楚我做错了什么,以及为什么我没有喝足够多的酒时才发现。

应一些没看到错误的人的要求,我们给出了更正后的 SQL。很明显,star_idand email_id、 orstation_idstation_area_id可能不是有效的比较。如果 SQL 有一个合适的类型系统,这条 SQL 根本就无法编译。

SELECT thread.*
  FROM email thread
  JOIN email selected      ON selected.email_id      = thread.email_id
  JOIN character recipient ON recipient.character_id = thread.recipient_id
  -- station_area_id = character_id is probably wrong
  JOIN station_area sa     ON sa.station_area_id     = recipient.character_id
  -- station_id = station_area_id is probably wrong
  JOIN station st          ON st.station_id          = sa.station_area_id
  -- star_id = email_id is probably wrong
  JOIN star origin         ON origin.star_id         = thread.email_id
  JOIN star destination    ON destination.star_id    = st.star_id
LEFT JOIN route
       ON ( route.from_id = origin.star_id
            AND 
            route.to_id = destination.star_id )
 WHERE selected.email_id          = ?
   AND ( thread.sender_id         = ? 
         OR ( thread.recipient_id = ?
              AND ( origin.star_id = destination.star_id
                    OR ( route.distance IS NOT NULL
                         AND
                         now() >= thread.datesent
                         + ( route.distance * interval '30 seconds' )
        ))))
ORDER BY datesent ASC, thread.parent_id ASC
Enter fullscreen mode Exit fullscreen mode

帮自己一个忙,用全名作为 ID。以后你再感谢我吧。你的 DBA 现在会感谢你的。

避免歧义

尽可能使用描述性极强的列名。例如,temperature对于以下情况,列名没有意义:

SELECT name, 'too cold'
  FROM areas
 WHERE temperature < 32;  
Enter fullscreen mode Exit fullscreen mode

我住在法国,对这里的人来说,32 度“太热”了。所以,把那一列命名为fahrenheit

SELECT name, 'too cold'
  FROM areas
 WHERE fahrenheit < 32;  
Enter fullscreen mode Exit fullscreen mode

现在已毫无疑问了。

另外,当你有外键约束时,如果可能的话,应该将约束两侧的列命名为相同的名称。例如,考虑一下这个完全合理、健全的 SQL。

SELECT *
  FROM some_table       s
  JOIN some_other_table o
    ON o.owner = s.person_id;
Enter fullscreen mode Exit fullscreen mode

看起来不错,其实没什么问题。但是,当你查看表定义时,发现some_other_table.owner有一个针对 的外键约束companies.company_id。这条 SQL 语句实际上是错的。如果你使用了相同的名称:

SELECT *
  FROM some_table       s
  JOIN some_other_table o
    ON o.company_id = s.person_id;
Enter fullscreen mode Exit fullscreen mode

再次,我们避免了歧义。我们可以立即清楚地知道我们有一个错误,并且您可以在一行代码中看到它,而无需查阅表定义。

但是,需要注意的是,这并非总是可行的。如果您有一个包含源仓库和目标仓库的表,您可能需要将source_iddestination_id与您的 进行比较warehouse_id。将它们命名为source_warehouse_iddestination_warehouse_id会使操作更容易理解。

还要注意,在上面的例子中,owner比 更能描述意图company_id。如果您觉得这可能会引起混淆,可以将列命名为owning_company_id。这样仍然可以将列的含义嵌入到名称中,同时强烈暗示其意图。

概括

命名标准非常重要,因为它有助于保持代码的一致性和可预测性。任何违反这种可预测性的行为都容易受到质疑。然而,精心设计的命名标准可以使代码更易于理解,如果做得好,通常甚至可以在一行代码中发现错误。

  • 使用underscore_names而不是CamelCase
  • 表名应为复数
  • 拼写出 id 字段(item_id而不是id
  • 不要使用含糊不清的列名
  • 如果可能,将外键列命名为与其引用的列相同的名称

虽然并不完美,但上述数据库命名约定将使您的数据库世界变得更美好。

文章来源:https://dev.to/ovid/database-naming-standards-2061
PREV
重新思考现代网络
NEXT
20 个 JavaScript 单行代码助你写出专业代码