1. 没有使用适当的指数
这是一个相对容易的问题,但它仍然经常发生。外键应该有索引。如果您使用的是WHERE
你应该(可能)有一个索引。此类索引通常应根据您需要执行的查询覆盖多个列。
2. 不强制引用完整性
您的数据库可能会有所不同,但如果您的数据库支持引用完整性(这意味着所有外键都保证指向存在的实体),您应该使用它。
在 MySQL 数据库上这种故障很常见。我不相信 MyISAM 支持它。 InnoDB 确实如此。您会发现有人正在使用 MyISAM 或正在使用 InnoDB 但无论如何都不使用它。
更多这里:
- 如果我总是使用 php 控制数据库输入,那么像 NOT NULL 和 FOREIGN KEY 这样的约束有多重要? https://stackoverflow.com/questions/382309/how-important-are-constraints-like-not-null-and-foreign-key-if-ill-always-contr
- 数据库设计中外键真的有必要吗? https://stackoverflow.com/questions/18717/are-foreign-keys-really-necessary-in-a-database-design
- 数据库设计中外键真的有必要吗? http://www.diovo.com/2008/08/are-foreign-keys-really-necessary-in-a-database-design/
3. 使用自然主键而不是代理(技术)主键
自然键是基于(表面上)唯一的外部有意义的数据的键。常见示例有产品代码、两个字母的州代码(美国)、社会安全号码等。代理主键或技术主键是那些在系统之外完全没有意义的主键。它们纯粹是为了识别实体而发明的,通常是自动递增字段(SQL Server、MySQL 等)或序列(最著名的是 Oracle)。
我认为你应该always使用代理键。这个问题出现在这些问题中:
- 您觉得您的主键怎么样? https://stackoverflow.com/questions/404040/how-do-you-like-your-primary-keys
- 表中主键的最佳实践是什么? https://stackoverflow.com/questions/337503/whats-the-best-practice-for-primary-keys-in-tables
- 在这种情况下您会使用哪种格式的主键。 https://stackoverflow.com/questions/506164/which-format-of-primary-key-would-you-use-in-this-situation
- 代理与自然/业务密钥 https://stackoverflow.com/questions/63090/surrogate-vs-natural-business-keys
- 我应该有一个专门的主键字段吗? https://stackoverflow.com/questions/166750/should-i-have-a-dedicated-primary-key-field
这是一个有些争议的话题,你不会得到普遍的同意。虽然您可能会发现有些人认为自然键在某些情况下是可以的,但除了可以说是不必要的之外,您不会发现对代理键有任何批评。如果你问我的话,这只是一个很小的缺点。
记住,甚至国家可能不复存在 http://en.wikipedia.org/wiki/ISO_3166-1(例如南斯拉夫)。
4. 编写需要的查询DISTINCT
to work
您经常在 ORM 生成的查询中看到这一点。查看 Hibernate 的日志输出,您将看到所有查询都以以下内容开头:
SELECT DISTINCT ...
这是确保不会返回重复行并因此获得重复对象的一种捷径。有时您也会看到人们这样做。如果你看到太多,那就是一个真正的危险信号。不是那个DISTINCT
不好或没有有效的应用程序。它确实(在这两方面)但它不是编写正确查询的替代品或权宜之计。
From 为什么我讨厌独特 http://weblogs.sqlteam.com/markc/archive/2008/11/11/60752.aspx:
当我的事情开始变坏的时候
意见是当开发人员
构建大量查询、连接
桌子放在一起,突然之间
他意识到这looks就像他一样
获取重复(甚至更多)行
他的立即反应...他的
这个“问题”的“解决方案”是
抛出 DISTINCT 关键字并POOF他所有的烦恼都会消失。
5. 优先考虑聚合而不是连接
数据库应用程序开发人员的另一个常见错误是没有意识到聚合的成本有多少(即GROUP BY
子句)可以与连接进行比较。
为了让您了解这种现象的普遍性,我已经多次就此主题撰写文章,但遭到了很多否决。例如:
From SQL 语句 - “join” 与 “group by 和having” https://stackoverflow.com/questions/477006/sql-statement-join-vs-group-by-and-having/477013#477013:
第一个查询:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
查询时间:0.312秒
第二次查询:
SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1
查询时间:0.016秒
这是正确的。加入版本I
提议的是比
聚合版本。
6. 不通过视图简化复杂查询
并非所有数据库供应商都支持视图,但对于那些支持视图的数据库供应商来说,如果明智地使用它们可以大大简化查询。例如,在一个项目中我使用了通用当事人模型 http://www.tdan.com/view-articles/5014/用于客户关系管理。这是一种极其强大且灵活的建模技术,但可能会导致许多连接。在这个模型中有:
-
Party:人员和组织;
-
党的角色:各方所做的事情,例如雇员和雇主;
-
当事人角色关系:这些角色如何相互关联。
Example:
- Ted 是一个人,是 Party 的子类型;
- Ted 有多种角色,其中之一是 Employee;
- 英特尔是一个组织,是一方的子类型;
- 英特尔有很多角色,其中之一是雇主;
- 英特尔聘用了 Ted,这意味着他们各自的角色之间存在关联。
因此,有五个表连接在一起,将 Ted 与其雇主联系起来。您假设所有员工都是人(而不是组织)并提供此帮助视图:
CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id
突然之间,您可以在高度灵活的数据模型上获得所需数据的非常简单的视图。
7. 不清理输入
这是一个巨大的。现在我喜欢 PHP,但如果您不知道自己在做什么,那么创建容易受到攻击的网站真的很容易。没有什么比这更好的概括了小鲍比·泰布尔斯的故事 http://xkcd.com/327/.
用户通过 URL、表单数据提供的数据和饼干应始终被视为敌对和净化。确保您得到了您所期望的。
8. 不使用准备好的语句
准备好的语句是指编译一个查询减去插入、更新和操作中使用的数据。WHERE
条款,然后再提供。例如:
SELECT * FROM users WHERE username = 'bob'
vs
SELECT * FROM users WHERE username = ?
or
SELECT * FROM users WHERE username = :username
取决于您的平台。
我见过数据库因这样做而崩溃。基本上,每次任何现代数据库遇到新查询时,它都必须对其进行编译。如果它遇到以前见过的查询,则数据库有机会缓存已编译的查询和执行计划。通过大量执行查询,数据库有机会找出并相应优化(例如,通过将已编译的查询固定在内存中)。
使用准备好的语句还可以为您提供有关某些查询的使用频率的有意义的统计数据。
准备好的语句还可以更好地保护您免受 SQL 注入攻击。
9. 标准化不够
数据库规范化 http://en.wikipedia.org/wiki/Database_normalization基本上是优化数据库设计或如何将数据组织到表中的过程。
就在本周,我遇到了一些代码,其中有人内爆了一个数组并将其插入到数据库中的单个字段中。规范化将将该数组的元素视为子表中的单独行(即一对多关系)。
这也出现在存储用户 ID 列表的最佳方法 https://stackoverflow.com/questions/620645/best-method-for-storing-a-list-of-user-ids:
我在其他系统中看到该列表存储在序列化的 PHP 数组中。
但缺乏正常化有多种形式。
More:
- 标准化:多远才足够? http://www.techrepublic.com/article/normalization-how-far-is-far-enough/
- SQL 设计:为什么需要数据库规范化 http://www.sqlmag.com/Article/ArticleID/4887/sql_server_4887.html
10. 过度标准化
这似乎与前一点相矛盾,但正常化,就像许多事情一样,是一种工具。它是达到目的的手段,而不是目的本身。我认为许多开发人员忘记了这一点并开始将“手段”视为“目的”。单元测试就是一个典型的例子。
我曾经开发过一个系统,该系统为客户提供了巨大的层次结构,如下所示:
Licensee -> Dealer Group -> Company -> Practice -> ...
因此,您必须将大约 11 个表连接在一起才能获得任何有意义的数据。这是正常化走得太远的一个很好的例子。
更重要的是,仔细且深思熟虑的非规范化可以带来巨大的性能优势,但在执行此操作时必须非常小心。
More:
- 为什么过多的数据库规范化可能是一件坏事 http://www.selikoff.net/blog/2008/11/19/why-too-much-database-normalization-can-be-a-bad-thing/
- 数据库设计规范化要走多远? https://stackoverflow.com/questions/496508/how-far-to-take-normalization-in-database-design
- 何时不规范化 SQL 数据库 http://www.25hoursaday.com/weblog/CommentView.aspx?guid=cc0e740c-a828-4b9d-b244-4ee96e2fad4b
- 也许标准化并不正常 https://blog.codinghorror.com/maybe-normalizing-isnt-normal/
- 所有关于编码恐怖的数据库规范化争论之母 http://highscalability.com/mother-all-database-normalization-debates-coding-horror
11.使用专属弧线
独占弧是一种常见错误,其中使用两个或多个外键创建表,其中一个且只有一个可以为非空。大错。一方面,维护数据完整性变得更加困难。毕竟,即使具有引用完整性,也无法阻止设置两个或多个外键(尽管有复杂的检查约束)。
From :
我们强烈建议不要在任何地方进行独家弧形建设
可能,因为他们可能很难编写代码
并造成更多的维护困难。
12. 根本不对查询进行性能分析
实用主义占据主导地位,尤其是在数据库领域。如果你坚持原则以至于它们已经成为教条,那么你很可能犯了错误。以上面的聚合查询为例。聚合版本可能看起来“不错”,但其性能却很糟糕。性能比较应该结束争论(但事实并非如此),但更重要的是:首先发表这种不明智的观点是无知的,甚至是危险的。
13. 过度依赖 UNION ALL,尤其是 UNION 结构
SQL 术语中的 UNION 只是连接一致的数据集,这意味着它们具有相同的类型和列数。它们之间的区别在于 UNION ALL 是一个简单的串联,应尽可能首选,而 UNION 将隐式执行 DISTINCT 来删除重复的元组。
UNION 和 DISTINCT 一样,有其用武之地。有有效的申请。但如果您发现自己做了很多这样的事情,特别是在子查询中,那么您可能做错了什么。这可能是由于查询构造不佳或数据模型设计不当而迫使您执行此类操作。
UNION,特别是在连接或相关子查询中使用时,可能会损坏数据库。尽可能避免它们。
14. 在查询中使用 OR 条件
这可能看起来无害。毕竟,AND 是可以的。或者应该也可以吧?错误的。基本上是 AND 条件限制数据集,而 OR 条件grows但不是以一种适合优化的方式。特别是当不同的 OR 条件可能交叉时,从而迫使优化器有效地对结果进行 DISTINCT 操作。
Bad:
... WHERE a = 2 OR a = 5 OR a = 11
Better:
... WHERE a IN (2, 5, 11)
现在,您的 SQL 优化器可以有效地将第一个查询转换为第二个查询。但也可能不会。只是不要这样做。
15. 没有设计适合高性能解决方案的数据模型
这是一个很难量化的点。通常通过其效果来观察。如果您发现自己为相对简单的任务编写了粗糙的查询,或者查找相对简单的信息的查询效率不高,那么您的数据模型可能很差。
在某些方面,这一点总结了所有早期的内容,但它更像是一个警示故事,即像查询优化这样的事情通常应该先完成,而实际上应该先完成。首先,在尝试优化性能之前,您应该确保拥有良好的数据模型。正如高德纳所说:
过早的优化是万恶之源
16. 数据库事务的不正确使用
特定进程的所有数据更改都应该是原子的。 IE。如果操作成功,则操作完全成功。如果失败,数据将保持不变。 - 不应该有“半途而废”的改变。
理想情况下,实现这一点的最简单方法是整个系统设计应努力通过单个 INSERT/UPDATE/DELETE 语句支持所有数据更改。在这种情况下,不需要特殊的事务处理,因为您的数据库引擎应该自动执行此操作。
但是,如果任何进程确实需要将多个语句作为一个单元执行以保持数据处于一致状态,则需要适当的事务控制。
- 在第一个语句之前开始事务。
- 在最后一条语句之后提交事务。
- 出现任何错误时,回滚事务。而且很NB!不要忘记跳过/中止错误之后的所有语句。
还建议仔细注意数据库连接层和数据库引擎在这方面如何交互的微妙之处。
17. 不理解“基于集合”的范式
SQL 语言遵循适合特定类型问题的特定范例。尽管存在各种特定于供应商的扩展,该语言仍难以处理 Java、C#、Delphi 等语言中的微不足道的问题。
这种缺乏理解表现在几个方面。
- 不恰当地在数据库上强加过多的过程或命令逻辑。
- 游标使用不当或过度。特别是当单个查询就足够了时。
- 错误地假设触发器在多行更新中受影响的每行触发一次。
确定明确的责任分工,力争使用合适的工具解决每个问题。