我有一些想法,一些是我随着时间积累的,但我真的很想知道是什么让你在建模数据库时顺利进行:
- 表名匹配主键名和描述键
- 模式按功能区域划分
- 尽可能避免使用复合主键(使用唯一约束)
- Camel Case 表名和字段名
- 不要使用 tbl_ 为表添加前缀,也不要使用 SP_ 为 procs 添加前缀(没有匈牙利符号)
- OLTP 数据库应该至少在 BCNF / 4NF
我有一些想法,一些是我随着时间积累的,但我真的很想知道是什么让你在建模数据库时顺利进行:
我还没有看到提到的一件事:
切勿使用数据库关键字作为对象名称。您不希望每次使用它们时都必须对其进行限定
如果您在创建时拼写错误,请在发现后立即修复。不必花费数年时间记住在此表中 UserName 确实是 Usernmae。当没有太多针对它编写的代码时,它更容易修复。
永远不要使用隐含连接(逗号语法),始终指定连接。
将每个人的意见放在一个列表中。
命名标准
数据类型
在代码中
文档
规范化和参照完整性
维护:运行定期脚本以查找
做个好人
我对 Oracle 的标准是:
对于 SQL Server,唯一的修改是对数据库对象名称使用驼峰式大小写(即 PartyName 而不是 party_name)。
查询将倾向于多行编写,每行一个子句或条件:
SELECT field1, field2, field2
FROM tablename t1
JOIN tablename2 t2 ON t1.id = t2.tablename_id
WHERE t1.field1 = 'blah'
AND t2.field2 = 'foo'
如果 SELECT 子句足够长,我将把它分成每行一个字段。
不要忘记定期备份您的数据库。
如果数据库用于特定应用程序,则有一个版本表,以便可以根据代码版本检查数据库版本(以及其他原因)。
不要在字段名称中使用类型名称。年长的人会记得 lpszFieldName 的旧 MS 标准和随之而来的愚蠢。
使用符合正常语言约定的描述性字段名称。例如“FirstName”而不是“NameFirst”
字段名称中的每个单词都大写
没有下划线
不要使用“索引”等普通关键字
不要在 ANYTHING 前加上对象类型。例如,我们不使用 tblCustomers 或 spCustomersGet。这些不允许良好的排序并提供零值。
使用模式来定义数据库的不同区域。例如sales.Customers 和hr.Employees。这将摆脱人们使用的大多数前缀。
任何类型的循环都应该被怀疑地看待。通常有更好的基于集合的方式。
对复杂的连接使用视图。
尽可能避免复杂的连接。拥有一个 CustomerPhoneNumbers 表可能更美观;但老实说,我们真的需要存储多少电话号码?只需将字段添加到客户表。您的数据库查询会更快,也更容易理解。
如果一个表调用字段“EmployeeId”,那么引用它的每个表都应该使用该名称。它不需要仅仅因为它在扩展表中就被称为 CustomerServiceRepId。
几乎所有表格都有“s”结尾。例如:客户、订单等。毕竟该表包含许多记录......
使用分析工具评估您的查询、索引和外键关系。甚至那些可能为您生成的。你可能会感到惊讶。
支持多对多关系的链接表在名称中包含两个链接表。例如,SchoolsGrades。通过表名很容易判断它的作用。
始终如一。如果你从一个约定的路径开始,除非你愿意重构所有以前的工作,否则不要半途而废。这应该制止任何“如果......不是很好”的想法,这些想法最终会导致混乱和大量的返工。
打字前三思。你真的需要那个表、字段、存储过程或视图吗?你确定它没有被其他地方覆盖吗?在添加之前获得共识。如果由于某种原因您必须将其取出,请先与您的团队交谈。我一直在 DBA 每天进行重大更改而不考虑开发人员的地方。这不好玩。
我总是尽量不使用字段名称中的类型——“sFirstName”、“sLastName”或“iEmployeeID”。虽然它们一开始是匹配的,但如果发生变化,它们就会不同步,并且以后更改这些名称是一件非常头疼的事情,因为您还必须更改依赖对象。
Intellisense 和 GUI 工具使找出列的类型变得很简单,所以我认为这没有必要。
WITH 子句确实有助于将查询分解为可管理的部分。
它还确实有助于提高查询执行计划的效率。
确保每个 varchar/nvarchar 选择都是合适的。
确保每个 NULLable 列选择是适当的 - 尽可能避免 NULLable 列 - 允许 NULL 应该是合理的位置。
无论您可能从此处的建议中使用任何其他规则,我都会在数据库中创建一个存储过程,该过程可以定期运行以确定您拥有的任何规则或标准的系统健康状况(其中一些是一点 SQL - 服务器特定):
在由于某种原因无法使用 DBMS 系统的参照完整性的任何情况下查找孤立记录(在我的系统中,我有一个进程表和一个测试表 - 所以我的 system_health SP 会查找没有测试的进程,因为我只有一个单向 FK 关系)
查找空模式
查找没有主键的表
查找没有任何索引的表
查找没有文档的数据库对象(我们使用 SQL Server 扩展属性将文档放入数据库中 - 该文档可以像列一样细化)。
查找特定于系统的问题 - 需要归档的表、不属于正常每月或每日处理的异常、具有或不具有默认值的某些常见列名(例如,CreateDate)。
寻找非确定性 UDF
查找 TODO 注释以确保数据库中的代码没有未经测试或预发布的代码。
所有这些都可以自动化,让您全面了解系统健康状况。
A few likes and dislikes.
My opinion is prefixes are horrible in every aspect. I currently work on a system where the tables are prefixed, and the columns within the tables are prefixed with 2 letter table name acronyms, I waste at least 30 mins each day working on this database because the acronym isn't logical. If you want to denote something with a prefix use a schema owner instead.
Using NVarchar from the start of a project if there is even a slight hint that down the line the text data will need to support multi lingual chars. Upgrading large databases because of lack of forward planning and thinking is a pain and wastes time.
Splitting each condition within a where clause onto a new line for readability (in and not in statements wrapped in brackets and tabbed in.) I think this is the important standard for me.
I worked at one company where a standard was that comma's must always be placed at the start of a line when performing parameter or variable declarations. This apparently made it more readable however I found it a complete nightmare.
每个人都以相同的基本格式编写 SQL 查询(视图、存储过程等)。它确实有助于开发/维护工作。
一致的命名标准。让每个人都在同一页面上,使用相同的格式(无论是驼峰式、特定前缀等)有助于准确维护系统。
良好的数据库设计和规范化。
除了归一化为 3NF 或 BCNF(有关此问题的更多信息)之外,我发现以下内容很有用:
所以“People”表有一个“PersonID”列。
表格格式的 SQL。
select a.field1, b.field2
from any_table a
inner join blah b on b.a_id = a.a_id
inner join yet_another y on y.longer_key = b.b_id
where a.field_3 > 7
and b.long_field_name < 2;
其中一部分是使用统一的长别名(在示例中,这里的 a、b 和 y 的长度都是 1)。
使用这种格式,我可以更快地回答常见问题,例如“什么表被 'a' 别名了?” 和“哪些字段将表 T 连接到查询中?” 该结构不需要很长时间来应用或更新,我发现它节省了很多时间。我们花在阅读代码上的时间多于编写代码。
其他一些(尽管很小)评论要靠墙扔...
SQL Server 数据库架构可用于组织表和存储过程以及控制安全性。
每个事务表都应始终跟踪创建记录的人员和时间,并在单独的列中更新记录。我见过简单地使用“更新日期”的实现,这可能会导致未来的审计挑战。
对具有离线/同步要求的项目的所有行使用 GUID 作为行标识符。
最重要的标准是:默认没有数据库。我发现有太多的开发人员为那些没有数据库的项目(至少现在)生活会容易得多。它只是工具箱中的一个工具,并不是每个问题都是钉子。
不恰当地使用数据库会导致领域模型贫乏、代码可测试性差和不必要的性能问题。
13-评估您的查询
确实如此。有时你得不到你想要的。
对我来说,用它们的确切内容和(对我们)用清晰的西班牙语命名表和字段总是有用的,并使用大驼峰大小写,没有空格:
用户名:NombreUsuario
名:阿佩里多帕特诺
第二姓:ApellidoMaterno
等等等等
避免愚蠢的缩写约定,例如积极鼓励像 EMP_ID_CONV_FCTR_WTF_LOL_WAK_A_WAK_HU_HU 这样的怪物的综合缩写字典。这条规则的灵感来自我以前见过的一套真实的指导方针。
在 MS-SQL 中,我一直拥有 dbo 拥有的对象,并且我在对这些对象的调用前加上 dbo。
太多次我看到我们的开发人员想知道为什么他们不能调用他们无意中拥有的对象。
表名匹配主键名和描述键
在同意这一点多年之后,我最近才跳槽,现在每张桌子上都有一个“ID”列。
是的,我知道,在链接表时它是模棱两可的!但是将 ProductID 链接到 ProductID 也是如此,所以,为什么要额外输入?
这个:
SELECT p.Name, o.Quantity FROM Products p, Orders o WHERE o.ProductID = p.ID
比这稍微好一点:
SELECT p.Name, o.Quantity FROM Products p, Orders o WHERE o.ProductID = p.ProductID
请注意,两者都需要表或别名前缀。但不仅我输入的字数略少(乘以数十个具有长描述性名称的表,并且它在数据密集型应用程序中快速加起来)而且它还可以更容易地知道哪个表是每个连接中的父表,哪个,在查询中加入 8-10 个表时,可以提供很多帮助。
用“数据库”来表示“SQL 产品”,我的回答是,“太多了,不胜枚举。你可以写一本关于这个主题的整本书。” 令人高兴的是,有人拥有。
我们使用 Joe Celko 的 SQL 编程风格 (ISBN 978-0120887972):“本书是启发式和规则、提示和技巧的集合,将帮助您提高 SQL 编程风格和熟练程度,并用于格式化和编写可移植、可读、可维护的SQL 代码。”
这种方法的优点包括:
在实践中,我们确实偏离了书中的规定,但令人惊讶的是很少。
记录一切;wiki 类型的文档易于设置并且软件是免费的。
确保您首先了解界面,然后再设计数据库。大多数情况下,最好知道您将要使用的数据如何工作,然后设计数据库。大多数糟糕的数据库设计发生在事情发展不提前的时候。
然后定义您要使用的数据库标准和版本。定义代码元素(视图、函数等)、数据库命名的标准;列、表的命名约定;列的类型约定;编码模板。
花时间考虑如何定义具有标准数据库类型的字段或定制类型的类型是预先整理好的事情。
作为文档的一部分,包括应用程序的注意事项列表和注意事项列表,其中包括您喜欢的讨厌的功能光标、触发器。
定期审查。
我在这里遵循许多与其他人相同的约定,但我想说一些尚未说的事情。
无论您喜欢表的复数名称还是单数名称,都要保持一致。选择其中一个,但不要同时使用。
表中的主键与表同名,后缀为_PK。外键与其对应的主键同名,但后缀为 _FK。例如,Product 表的主键称为 Product_PK;在 Order 表中,对应的外键是 Product_FK。我从我的另一个 DBA 朋友那里养成了这个习惯,到目前为止我很喜欢它。
每当我执行 INSERT INTO...SELECT 时,我都会为 SELECT 部分中的所有列设置别名,以匹配 INSERT INTO 部分中的列名,以便更容易维护并查看事情如何匹配。
我同意你放在那里的所有东西,除了#5。我经常为表和存储过程使用前缀,因为我们开发的系统有很多不同的功能区域,所以我倾向于在表和存储过程前加上一个标识符,这将允许它们在 Management Studio 中根据什么区域很好地分组他们属于。
示例:cjso_Users、cjso_Roles,然后你有 routing_Users、routing_Roles。这听起来像是数据复制,但实际上两个不同的用户/角色表用于系统的完全独立的功能(cjso 将用于基于客户的电子商务应用程序,而路由将代表使用路由的员工和分销商系统)。
我喜欢我们的表命名约定:
People Table
PEO_PersonID
PEO_FirstName
...
这有助于使更大的查询更具可读性。并加入更有意义:
Select * -- naughty!
From People
Join Orders on PEO_PersonID = ORD_PersonID
--...
我想而不是命名约定是命名的一致性。