数据库命名标准
介绍
表名称
列名
概括
介绍
尽管 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
(顺便说一句,是的,我确实非常小心我的 SQL 格式)
你看出问题了吗?如果 SQL 语句使用了完整的 ID 名称,例如email_id
、star_id
和,那么在我输入这条 SQL 语句时station_id
,这些错误就会非常明显,而不是等到我试图弄清楚我做错了什么,以及为什么我没有喝足够多的酒时才发现。
应一些没看到错误的人的要求,我们给出了更正后的 SQL。很明显,star_id
and email_id
、 orstation_id
和station_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
帮自己一个忙,用全名作为 ID。以后你再感谢我吧。你的 DBA 现在会感谢你的。
避免歧义
尽可能使用描述性极强的列名。例如,temperature
对于以下情况,列名没有意义:
SELECT name, 'too cold'
FROM areas
WHERE temperature < 32;
我住在法国,对这里的人来说,32 度“太热”了。所以,把那一列命名为fahrenheit
。
SELECT name, 'too cold'
FROM areas
WHERE fahrenheit < 32;
现在已毫无疑问了。
另外,当你有外键约束时,如果可能的话,应该将约束两侧的列命名为相同的名称。例如,考虑一下这个完全合理、健全的 SQL。
SELECT *
FROM some_table s
JOIN some_other_table o
ON o.owner = s.person_id;
看起来不错,其实没什么问题。但是,当你查看表定义时,发现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;
再次,我们避免了歧义。我们可以立即清楚地知道我们有一个错误,并且您可以在一行代码中看到它,而无需查阅表定义。
但是,需要注意的是,这并非总是可行的。如果您有一个包含源仓库和目标仓库的表,您可能需要将source_id
和destination_id
与您的 进行比较warehouse_id
。将它们命名为source_warehouse_id
和destination_warehouse_id
会使操作更容易理解。
还要注意,在上面的例子中,owner
比 更能描述意图company_id
。如果您觉得这可能会引起混淆,可以将列命名为owning_company_id
。这样仍然可以将列的含义嵌入到名称中,同时强烈暗示其意图。
概括
命名标准非常重要,因为它有助于保持代码的一致性和可预测性。任何违反这种可预测性的行为都容易受到质疑。然而,精心设计的命名标准可以使代码更易于理解,如果做得好,通常甚至可以在一行代码中发现错误。
- 使用
underscore_names
而不是CamelCase
- 表名应为复数
- 拼写出 id 字段(
item_id
而不是id
) - 不要使用含糊不清的列名
- 如果可能,将外键列命名为与其引用的列相同的名称
虽然并不完美,但上述数据库命名约定将使您的数据库世界变得更美好。
文章来源:https://dev.to/ovid/database-naming-standards-2061