应用程序开发人员犯的数据库开发错误


566

应用程序开发人员常犯哪些常见的数据库开发错误?


Answers:


1002

1.不使用适当的索引

这是一个相对容易的过程,但仍然会一直发生。外键上应该有索引。如果您在中使用字段,则WHERE应该(可能)在上面有一个索引。此类索引通常应根据您需要执行的查询覆盖多个列。

2.不强制参照完整性

您的数据库可能在这里有所不同,但是如果您的数据库支持参照完整性(意味着保证所有外键都指向存在的实体),则应该使用它。

在MySQL数据库上看到此失败很常见。我不相信MyISAM支持它。InnoDB可以。您会发现正在使用MyISAM的人或正在使用InnoDB的人,但仍然没有使用它。

更多内容:

3.使用自然而不是替代(技术)主键

自然键是基于(表面上)唯一的外部有意义数据的键。常见的示例是产品代码,两个字母的州代码(US),社会保险号等。代理或技术主键是那些在系统外部完全没有意义的键。它们纯粹是为了识别实体而发明的,通常是自动递增的字段(SQL Server,MySQL,其他)或序列(最著名的是Oracle)。

我认为您应该始终使用代理键。这些问题出现在以下问题中:

这是一个颇有争议的话题,您将无法达成普遍共识。尽管您可能会发现某些人认为自然键在某些情况下还可以,但您可以发现对替代键的任何批评,除了可以认为没有必要。如果你问我,那是一个很小的缺点。

请记住,甚至国家也可能不复存在(例如,南斯拉夫)。

4.编写需要DISTINCT工作的查询

您经常在ORM生成的查询中看到这一点。查看Hibernate的日志输出,您将看到所有查询均以以下内容开头:

SELECT DISTINCT ...

这是确保您不返回重复的行并因此获得重复的对象的捷径。有时您还会看到人们也在这样做。如果您看到的太多,那是一个真正的危险信号。这DISTINCT并不坏,也没有有效的应用程序。它确实(在两个方面都有),但这不是编写正确查询的替代或权宜之计。

我为何讨厌DISTINCT

在我看来,事情开始变得糟糕的是,当开发人员正在构建大量查询,将表连接在一起时,突然间他意识到自己似乎正在获得重复(甚至更多)的行和他的立即响应。他对这个“问题”的“解决方案”是使用DISTINCT关键字,POOF消除了 所有麻烦。

5.有利于聚集而不是联接

数据库应用程序开发人员的另一个常见错误是,没有意识到GROUP BY与联接相比,可以使用多少昂贵的聚合(即子句)。

为了让您了解它的普及程度,我在这里多次写了这个话题,对此深表歉意。例如:

SQL语句-“ join”与“ group by and have”

第一个查询:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

查询时间:0.312 s

第二个查询:

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 s

那就对了。我建议的连接版本比聚合版本快二十倍。

6.不通过视图简化复杂的查询

并非所有数据库供应商都支持视图,但对于所有支持视图的视图,如果明智地使用它们,则可以大大简化查询。例如,在一个项目中,我为CRM 使用了通用的Party模型。这是一种非常强大且灵活的建模技术,但是会导致许多连接。在此模型中:

  • :人民和组织;
  • 当事人角色:当事人所做的事情,例如雇员和雇主;
  • 派对角色关系:这些角色如何相互关联。

例:

  • 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,但是如果您不知道自己在做什么,那么很容易创建容易受到攻击的站点。没有什么比小鲍比桌子故事更能说明问题的了。

用户通过URL,表单数据和cookie提供的数据应始终视为敌对和消毒处理。确保您得到了期望的东西。

8.不使用准备好的陈述

准备语句是指您在编译查询时减去插入,更新和WHERE子句中使用的数据,然后在以后提供的数据。例如:

SELECT * FROM users WHERE username = 'bob'

SELECT * FROM users WHERE username = ?

要么

SELECT * FROM users WHERE username = :username

取决于您的平台。

我已经看到数据库通过这样做而屈服。基本上,每当任何现代数据库遇到新查询时,都必须对其进行编译。如果它遇到以前遇到的查询,则为数据库提供了缓存已编译查询和执行计划的机会。通过大量执行查询,您将使数据库有机会找出并进行相应的优化(例如,通过将编译后的查询固定在内存中)。

使用准备好的语句还将为您提供有关使用某些查询的频率的有意义的统计信息。

准备好的语句还可以更好地保护您免受SQL注入攻击。

9.不够规范

数据库规范化基本上是优化数据库设计或将数据组织到表中的过程。

就在本周,我遇到了一些代码,其中有人将一个数组内插并插入到数据库的单个字段中。规范化该处理将把该数组的元素视为子表中的单独行(即一对多关系)。

这也想出了在最佳方法用于存储用户ID列表

我在其他系统中看到过该列表存储在序列化的PHP数组中。

但是缺乏规范化的形式很多。

更多:

10.标准化过多

这似乎与上一点矛盾,但是规范化(就像许多事物一样)是一种工具。它本身就是达到目的而不是目的的一种手段。我认为许多开发人员都忘记了这一点,并开始将“方法”视为“目的”。单元测试就是一个很好的例子。

我曾经在一个具有庞大客户层次结构的系统上工作,例如:

Licensee ->  Dealer Group -> Company -> Practice -> ...

这样一来,您必须将大约11个表连接在一起,才能获取任何有意义的数据。这是标准化过度的一个很好的例子。

更重要的是,仔细和考虑周到的非规范化可以带来巨大的性能优势,但是在执行此操作时必须非常小心。

更多:

11.使用排他弧

如果用两个或多个外键创建一个表,并且其中一个只能为非空,则排他弧是一个常见错误。 大错。 一方面,维护数据完整性变得更加困难。毕竟,即使具有参照完整性,也无法阻止设置两个或多个这些外键(尽管有复杂的检查约束)。

实用指南到关系数据库设计

我们强烈建议尽量不要使用专有的弧线构造,这是有充分理由的,因为这样做可能会使他们难以编写代码并带来更多的维护困难。

12.根本不对查询进行性能分析

实用主义至高无上,特别是在数据库领域。如果您坚持原则直到它们已成为教条,那么您很可能犯了错误。以上面的聚合查询为例。聚合版本可能看起来“不错”,但是其性能却很糟糕。性能比较应该已经结束了辩论(但是还没有结束),但更重要的是:首先冒出这种不了解情况的观点是无知的,甚至是危险的。

13.过度依赖UNION ALL,尤其是UNION构造

用SQL术语表示的UNION仅连接一致的数据集,这意味着它们具有相同的类型和列数。它们之间的区别是UNION ALL是一个简单的串联,应该尽可能地被优先使用,而UNION将隐式执行DISTINCT来删除重复的元组。

像DISTINCT这样的UNION都有自己的位置。有有效的申请。但是,如果您发现自己做了很多事情,尤其是在子查询中,那么您可能做错了什么。这可能是由于查询构造不良或数据模型设计不良而迫使您执行此类操作的情况。

UNION,尤其是在联接或从属子查询中使用时,可能会使数据库瘫痪。尽可能避免它们。

14.在查询中使用OR条件

这似乎无害。毕竟,AND是可以的。还是应该还可以吧?错误。基本上,“与”条件会限制数据集,而“或”条件会增长数据集,但不会以适合自己的方式进行优化。特别是当不同的OR条件可能相交时,从而迫使优化器对结果进行有效的DISTINCT运算。

坏:

... WHERE a = 2 OR a = 5 OR a = 11

更好:

... WHERE a IN (2, 5, 11)

现在,您的SQL优化器可以有效地将第一个查询转换为第二个查询。但事实并非如此。只是不要这样做。

15.不设计其数据模型以使其适用于高性能解决方案

这很难量化。通常通过其效果来观察。如果您发现自己为相对简单的任务编写粗糙的查询,或者发现相对简单的信息的查询效率不高,那么您的数据模型可能很差。

在某种程度上,这一点总结了所有较早的内容,但更多的是一个警告性的故事,即通常应首先执行诸如查询优化之类的事情,而其次才应执行。首先,在尝试优化性能之前,应确保您具有良好的数据模型。正如Knuth所说:

过早的优化是万恶之源

16.错误使用数据库事务

特定过程的所有数据更改都应该是原子的。即,如果操作成功,它将完全完成。如果失败,则数据保持不变。-不应有“半完成”更改的可能性。

理想情况下,实现此目标的最简单方法是整个系统设计应努力通过单个INSERT / UPDATE / DELETE语句来支持所有数据更改。在这种情况下,不需要特殊的事务处理,因为数据库引擎应该自动执行。

但是,如果任何进程确实要求将多个语句作为一个单元执行以使数据保持一致状态,则必须进行适当的事务控制。

  • 在第一个语句之前开始事务。
  • 在最后一条语句之后提交事务。
  • 发生任何错误时,请回滚事务。而且非常NB!不要忘记跳过/中止错误后的所有语句。

还建议您特别注意数据库连接层和数据库引擎在这方面如何交互的子属性。

17.不了解“基于集合”的范例

SQL语言遵循适用于特定类型问题的特定范例。尽管有各种特定于供应商的扩展,但该语言仍在努力解决Java,C#,Delphi等语言中的琐碎问题。

这种缺乏理解以几种方式表现出来。

  • 在数据库上不适当地施加了过多的过程性或命令性逻辑。
  • 游标使用不当或过多。尤其是当一个查询就足够时。
  • 错误地假设触发器触发多行更新中每行触发一次。

确定明确的责任分工,并努力使用适当的工具来解决每个问题。


9
在有关外键的MySQL语句上,您是正确的,MyISAM不支持它们,但您暗示仅使用MyISAM是错误的设计。我使用MyISAM的原因是InnoDB不支持全文本搜索,我也不认为这是不合理的。
德里克·H

1
我要问#6。使用这样的视图是我最喜欢做的事情之一,但是我最近很震惊地了解到,只有在视图结构允许使用合并算法的情况下,使用MySQL的基础表上的索引才会被遵守。否则,将使用临时表,并且所有索引均无用。当您意识到大量操作导致此行为时,这甚至会更加令人震惊。这是将.01秒查询转换为100秒查询的好方法。这里的其他人有经验吗?检查下一条评论中的链接。
彼得·贝利

5
完全不同意#3。是的,国家可以不复存在,但是国家/地区代码将继续代表同一件事。与货币代码或美国州相同。在这些情况下使用代理键是愚蠢的,并且由于必须包含额外的联接,因此在查询中产生更多的开销。我要说的是,您可能应该对用户特定的数据(因此,而不是国家,货币和美国)使用代理代替。
托马斯

1
RE:#11强制执行数据完整性所需的检查约束很简单。避免该设计还有其他原因,但是对“复杂”检查约束的需求并不是其中之一。
托马斯

2
对于#3,您并不诚实。人工密钥的弊端比“您可能不需要它”更多。具体来说,使用自然键将使您能够控制将表中的数据写入磁盘的顺序。如果您知道如何查询表,则可以对它建立索引,以便同时访问的行将最终出现在同一页面中。此外,您可以使用唯一的复合索引来强制数据完整性。如果需要,除了人工密钥索引之外,还必须添加它。如果说综合指数是您的pkey,那就是用一只石头杀死2只鸟。
Shane H 2010年

110

开发人员犯的关键数据库设计和编程错误

  • 自私的数据库设计和使用。 开发人员通常将数据库视为他们的个人持久性对象存储,而不考虑数据中其他利益相关者的需求。这也适用于应用程序架构师。不良的数据库设计和数据完整性使第三方很难处理数据,并可能大大增加系统的生命周期成本。在应用程序设计中,Reporting和MIS往往不是一个很好的表亲,只是事后才想到的。

  • 滥用非规范化数据。过度使用非规范化数据并尝试在应用程序中进行维护是解决数据完整性问题的秘诀。谨慎使用非规范化。不想将联接添加到查询不是规范化的借口。

  • 害怕写SQL。 SQL不是火箭科学,实际上很擅长于完成其工作。O / R映射层非常擅长执行95%的简单查询,并且非常适合该模型。有时,SQL是完成这项工作的最佳方法。

  • 教条主义的“无存储过程”政策。 不管您是否认为存储过程都是邪恶的,这种教条主义的态度在软件项目中都没有。

  • 不了解数据库设计。 规范化是您的朋友,而不是火箭科学。 连接和基数是非常简单的概念-如果您参与数据库应用程序开发,那么确实没有任何不理解它们的借口。


2
有人可能会争辩说交易应该在交易数据库中完成,而报告和MIS应该在单独的分析数据库中完成。因此,您可以两全其美,每个人都很高兴(可怜的杯子除外,后者必须编写数据转换脚本以从前者中构建后者)。
克里斯·辛普森

不仅是编写ETL的烂摊子-任何使用系统数据的人,由于未实际记录几个关键关系而装箱的MIS应用程序中质量低劣的数据,还会涉及无休止的和解问题数据质量差。
ConcernedOfTunbridgeWells,2009年

我不可能完全不同意第一点。数据库用于持久性,而不用于进程间通信。对于这个问题几乎总是有更好的解决方案。除非有明确的要求,否则您绝对应该将数据库视为除您的应用程序之外没有人会使用它。即使有明确的要求,也可以对它进行一些用户案例和根本原因分析,您常常会发现一种更好的方式来满足请求者的意图。再说一次,我在某家公司工作,在该公司中,短语CQRS有点常见
George Mauer 2010年

3
一个简单的例子:我有一个保险单管理系统,需要将500万份索赔的状态加载到分割的再保险系统中,以计算潜在的追回金额。这些系统是较旧的客户端-服务器COTS软件包,旨在与较旧的大型机系统接口。出于财务控制目的,两者都必须对帐。该工作每月完成一次。按照您的逻辑,我将写一系列定义需求的用户案例,并要求供应商引用在将Web Service包装器添加到其现有产品时的报价。
ConcernedOfTunbridgeWells,2010年

2
然后,您的DBA要么懒惰要么无能。
ConcernedOfTunbridgeWells,

80
  1. 不对数据库架构使用版本控制
  2. 直接针对实时数据库工作
  3. 不阅读并理解更高级的数据库概念(索引,聚簇索引,约束,物化视图等)
  4. 无法测试可扩展性...仅3或4行的测试数据永远不会给您真实的现场表现

1
我紧随其后的是#1和#2。每当我对数据库进行更改时,我都会转储其模式并对其进行版本化;我有三个数据库设置,一个开发数据库,​​一个暂存数据库,以及一个实时数据库-在实时数据库上,没有任何东西经过“测试”!
Ixmatus 2010年

在Red Gate,我们已采取措施来改善您的SQL Source Control的第一点!通过研究期间的交谈,我认为人们不再使用生产数据库进行开发了,但是通常会进行“紧急”修复,这些修复通常可以回到开发环境,这是另一个问题。
David Atkinson,

46

过度使用和/或依赖存储过程。

一些应用程序开发人员将存储过程视为中间层/前端代码的直接扩展。这似乎是Microsoft堆栈开发人员的一个共同特征(我是其中一个,但我已经长大了),并且产生许多执行复杂的业务逻辑和工作流处理的存储过程。这在其他地方要好得多。

在已经证明某些实际技术因素需要使用存储过程(例如,性能和安全性)的情况下,存储过程很有用。例如,保持大型数据集的聚合/过滤“接近数据”。

最近,我不得不帮助维护和增强大型Delphi桌面应用程序,该应用程序中的70%的业务逻辑和规则是在1400 SQL Server存储过程(其余的UI事件处理程序)中实现的。这是一场噩梦,主要是由于难以向TSQL引入有效的单元测试,缺乏封装和较差的工具(调试器,编辑器)。

过去与Java团队一起工作时,我很快发现,在这种环境中,情况往往完全相反。Java架构师曾经告诉我:“数据库用于存储数据,而不是代码。”

这些天,我认为根本不考虑存储过程是一个错误,但是在提供有用好处的情况下,应谨慎使用(默认情况下不使用)(请参阅其他答案)。


4
在使用存储过程的任何项目中,存储过程往往会成为一个伤害之岛,因此一些开发人员制定了“无存储过程”规则。因此,它们之间似乎存在公开冲突。您的答案为何时选择一种或另一种方式提供了很好的理由。
沃伦·P 2010年

好处:安全-您不必赋予应用程序“从...中删除*”的功能;调整-DBA可以调整查询,而不必重新编译/部署整个应用程序;分析-在更改数据模型后很容易重新编译一堆proc以确保它们仍然有效;最后,考虑到SQL是由数据库引擎(而不是您的应用程序)执行的,那么“数据库是用于数据,而不是代码”的概念就受到了限制。
NotMe 2011年

因此,您会在UI中将业务逻辑与正在处理的数据分离开来吗?这似乎不是一个好主意,尤其是当数据处理由数据库服务器而不是通过UI进行往返时,效率最高。这也意味着控制应用程序更加困难,因为您不能依赖于数据库来控制其数据,并且可能拥有不同版本的UI,并且正在进行不同的数据操作。不好。除了通过存储过程,我什么都不能触碰我的数据。
David T. Macknet 2011年

如果需要将业务逻辑与UI分开,则可以使用多层体系结构。或者,具有业务对象和逻辑的库,供不同的应用程序/ UI使用。存储过程将您的数据/业务逻辑锁定到特定的数据库,在这种情况下更改数据库非常昂贵。巨大的成本是不好的。
太过

@too:在大多数情况下,更改数据库的成本非常高。不要忘记失去特定DBMS提供的性能和安全性功能的想法。此外,附加层会增加复杂性并降低性能,并且附加层将与您的特定语言绑定在一起。最后,与数据库服务器相比,所使用的语言更可能会更改。
NotMe 2011年

41

第一个问题?他们仅在玩具数据库上进行测试。因此,他们不知道当数据库变大时,他们的SQL将会爬网,因此必须有人来修复它(您听到的声音是我磨牙)。


2
数据库的大小是相关的,但是更大的问题是负载-即使您在真实的数据集上进行测试,也不会在数据库处于生产负载下时测试查询的性能,这可能会让您大开眼界。
davidcl 2011年

我会说数据库大小比负载大。我见过很多次,有失踪的关键指标-在测试过程中从来没有性能问题,因为整个数据库装入到内存中
多瑙河水手


28

相关子查询导致性能不佳

大多数时候,您想避免相关的子查询。如果在子查询中有对外部查询中列的引用,则子查询是相关的。发生这种情况时,对于返回的每一行,子查询至少执行一次,如果在应用了包含相关子查询的条件之后再应用其他条件,则子查询可以执行多次。

原谅人为的示例和Oracle语法,但让我们说,您想查找自上次商店一天的销售额不足10,000美元以来在您的任何商店中雇用的所有员工。

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

此示例中的子查询通过store_id与外部查询相关联,并且将为系统中的每个员工执行。可以优化此查询的一种方法是将子查询移动到内联视图。

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

在此示例中,from子句中的查询现在是一个内联视图(再次使用某些Oracle特定语法),并且仅执行一次。根据您的数据模型,此查询可能会执行得更快。随着员工人数的增加,它的性能将优于第一个查询。如果只有很少的员工和许多商店(也许很多商店没有雇员)并且在store_id上​​索引了daily_sales表,则第一个查询实际上可以更好地执行。这是不太可能的情况,但是显示了相关查询如何可能比替代查询更好地执行。

我已经看到初级开发人员多次关联子查询,并且通常会对性能产生严重影响。但是,在删除相关子查询时,请确保在查看前后的解释计划,以确保不会使性能恶化。


1
好点,并强调您的相关点之一-测试您的更改。学习使用说明性计划(并查看数据库实际上在执行查询的过程以及它的成本),在大型数据集上进行测试,并且不要使SQL过于复杂,难以理解/难以维护以进行优化实际上并不能改善实际性能。
罗伯·惠兰

21

以我的经验:
不与经验丰富的DBA通信。


17

使用Access代替“真实”数据库。有很多很棒的小型甚至免费数据库,例如SQL ExpressMySQLSQLite,它们可以更好地工作和扩展。应用程序通常需要以意想不到的方式进行扩展。


16

忘记在表之间建立关系。我记得我刚开始在现任雇主工作时必须进行清理。


14

使用Excel存储(大量)数据。

我已经看到公司拥有数千行并且使用多个工作表(由于Excel早期版本的行数限制为65535)。


Excel非常适合于报表,数据表示和其他任务,但不应视为数据库。


14

我想补充一下:在高性能代码上偏爱“优雅”代码。应用程序开发人员认为,最适合数据库的代码通常很难看。

认为废话过早的优化。数据库必须考虑原始设计和任何后续开发中的性能。在我看来,性能是数据库设计的50%(40%是数据完整性,最后10%是安全性)。一旦将真正的用户和真实的流量置于数据库中,那些不是自下而上构建的数据库将无法正常运行。过早的优化并不意味着没有优化!这并不意味着您应该编写几乎总是表现不佳的代码,因为您会发现它更容易使用(例如,除非所有其他方法都失败了,否则在生产数据库中绝对不允许使用光标)。这意味着除非需要,您无需关注挤出最后的性能。关于什么将在数据库上取得更好的性能,人们知道很多,


2
+1-数据库编程涉及优化机械组件的行为。但是请注意,Knuth说过早的优化是大约97%的时间(或类似的说法)所有邪恶的根源。数据库设计是您真正必须首先考虑这一方面的领域。
ConcernedOfTunbridgeWells,2009年

2
嗯...您在说的是不成熟的优化。从数据库设计的一开始就需要考虑实际使用情况(实际上也包括应用程序设计)。Knuth的规则实际上并非微不足道,因为您必须决定什么是过早的,什么不是过早的-这实际上归结为“没有数据就不执行优化”。您正在谈论的与性能相关的早期决策具有数据-某些设计将对未来性能设置不可接受的限制,您可以进行计算。
罗伯·惠兰

13

不使用参数化查询。他们在停止SQL注入方面非常方便。

这是另一个答案中提到的不清除输入数据的特定示例。


3
除了消毒输入是错误的。消毒意味着将其放置在可能存在危险的地方。参数化意味着将其完全排除在危害之外。
达斯汀

12

当开发人员使用嵌套的select语句甚至函数返回查询的“ SELECT”部分中的select语句的结果时,我会讨厌它。

实际上,我很惊讶我在这里没有其他地方看到此消息,也许我忽略了它,尽管@adam指出了类似的问题。

例:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

在这种情况下,如果MyTable返回10000行,则结果就好像该查询只运行了20001个查询一样,因为它必须运行初始查询,并为每行结果查询一次其他表。

开发人员可以在仅返回几行数据且子表通常只包含少量数据的开发环境中避免这种工作,但是在生产环境中,这种查询会成倍地成倍增加成本数据添加到表中。

一个更好的(不一定是完美的)示例如下所示:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

这使数据库优化器可以将数据混合在一起,而不是对主表中的每个记录重新查询,通常我会在需要修复已创建此问题的代码时发现,通常最终会使查询速度提高100%或同时减少CPU和内存使用量。


12

对于基于SQL的数据库:

  1. 没有利用CLUSTERED INDEXES或为CLUSTER选择了错误的列。
  2. 在父/子表关系中,不使用SERIAL(自动编号)数据类型作为PRIMARY KEY联接到FOREIGN KEY(INT)。
  3. 当插入或删除许多记录时,不在表上更新统计信息。
  4. 当插入或删除许多行时不重新组织(即,卸载,删除,重新创建,加载和重新索引)表(某些引擎实际上将带有删除标志的已删除行保留在表中)。
  5. 在具有高事务处理率的大表上不利用FRAGMENT ON EXPRESSION(如果支持)。
  6. 为列选择错误的数据类型!
  7. 没有选择正确的列名。
  8. 不在表末尾添加新列。
  9. 没有创建适当的索引来支持常用查询。
  10. 在可能值很少的列上创建索引,并创建不必要的索引。
    ...更多要添加。

1
一个小问题:2)实际上是不好的做法。我明白了您的意思-您想要该自动编号上的唯一索引,并将其用作代理键。但是主键不应该是自动编号,因为这不是主键的本质:主键是“记录的内容”,它(除了销售交易之类的东西)不是自动编号,而是一些唯一的位有关要建模的实体的信息。
David T. Macknet 2011年

对主键和外键使用自动编号的主要原因是为了确保可以保持父子联接,而无需考虑其他任何列的更改。使用其他主键(例如客户名称或其他数据)可能会带来风险!
弗兰克·R。

@David:我的立场是正确的!列作为定位行的有意义的主要对象!
Frank R.

归根结底,这是一个语义问题……Microsoft首选主键是无意义的,而不是有意义的。围绕它的争论激增,但我陷入了“有意义的”阵营。:)
David T. Macknet 2011年

9
  • 在解决生产数据库中的某些问题之前不进行备份。

  • 在存储过程中的存储对象(如表,视图)上使用DDL命令。

  • 害怕使用存储的proc或害怕在一个更有效/更适合使用的地方使用ORM查询。

  • 忽略数据库探查器的使用,它可以准确地告诉您最终将ORM查询转换为什么,从而在不使用ORM时验证逻辑甚至进行调试。


8

没有执行正确的标准化级别。您要确保不重复数据,并根据需要将数据拆分为不同的数据。你还需要确保你没有关注正常化远,这将影响性能。


多远才算远?如果没有数据重复,您将如何进行进一步处理?
finnw

标准化是删除冗余数据和增加灵活性与降低性能和增加复杂性之间的平衡。找到正确的平衡需要经验,并且会随着时间而变化。有关何时
取消

8

将数据库视为仅一种存储机制(例如,荣耀的集合库),因此从属于其应用程序(忽略共享数据的其他应用程序)


这样做的必然结果是将过多的查询工作分担给应用程序,而不是将其保留在它所属的数据库中。LINQ在这方面特别糟糕。
3Dave 2010年


8

1-不必要在where子句中的值上使用函数,而不会使用该索引的结果。

例:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

代替

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

在较小程度上:不将功能索引添加到需要它们的值中...

2-不添加检查约束以确保数据的有效性。约束可以由查询优化器使用,它们确实有助于确保您可以信任您的不变式。只是没有理由不使用它们。

3-出于纯粹的惰性或时间压力,将非规范化的列添加到表中。事物通常不是以这种方式设计的,而是演变成这种方式。毫无疑问,最终的结果是,当您为将来的发展中丢失的数据完整性所困扰时,试图清理混乱的工作量很大。

考虑到这一点,没有数据的表重新设计非常便宜。一个具有数百万条记录且没有完整性的表...重新设计并不是那么便宜。因此,在创建列或表时进行正确的设计将分摊。

4-与其说数据库本身,不如说它确实令人讨厌。不在乎SQL的代码质量。您的SQL以文本形式表示的事实并不能很好地将逻辑隐藏在字符串处理算法堆中。完全可以用其他程序员可以实际理解的方式以文本形式编写SQL。


7

之前已经说过,但是: 索引,索引,索引。我已经看到了很多情况下企业网络应用程序性能不佳的情况,这些情况仅通过做一些概要分析(以查看哪些表受到了很大的攻击),然后在这些表上添加索引即可解决。这甚至不需要SQL编写知识的方式,而且收益是巨大的。

避免像瘟疫一样重复数据。有人倡导一点重复不会造成伤害,但会提高性能。嘿,我并不是说您必须将架构折磨成“第三范式”,直到它如此抽象以至于DBA都不知道发生了什么。只要了解一下,只要您复制一组名称,邮政编码或运输代码,这些副本最终就会彼此失去同步。它会发生。然后,当您运行每周维护脚本时,您将大吃一惊。

最后:使用清晰,一致,直观的命名约定。以良好的代码片段应可读的方式,良好的SQL模式或查询应可读的,并实际上告诉您它在做什么,即使没有注释也是如此。六个月后,当您需要维护桌面时,您将感激不尽。 "SELECT account_number, billing_date FROM national_accounts"比“从NTNLACCTS生成BILLDAT SELECT ACCNTNBR”更容易使用。


如果正确设置它们,它们将不会,但这涉及使用许多人都过敏的触发器。
HLGEM,2009年

6

在运行DELETE查询之前(尤其是在生产数据库上),不执行相应的SELECT查询!


5

我二十年来最常见的错误:没有事先计划。许多开发人员将创建数据库和表,然后在构建应用程序时不断修改和扩展表。最终结果通常是一团糟且效率低下,以后很难清理或简化。


1
我可以想象在这种情况下会发生的恐怖……无模式数据库更适合快速原型设计和迭代开发,但是像其他所有东西一样,这种灵活性伴随着各种取舍。
ZsoltTörök,

4

a)对字符串中的查询值进行硬编码
b)将数据库查询代码放入Windows Forms应用程序的“ OnButtonPress”操作中

我都看过


4
“在Windows Form应用程序的“ OnButtonPress”操作中放入数据库查询代码”这里的数据库错误是什么?
递归

@recursive:这是一个巨大的SQL注入漏洞。任何人都可以将任意SQL发送到您的服务器,它将按原样运行。
Bill Karwin 09年

同意@recursive。这些确实与数据库问题无关。
p.campbell,2009年

b)是架构错误。当然,无论如何,直接在应用程序中编码查询是一个坏主意。
3Dave 2010年

4

没有对管理应用程序中的数据库连接给予足够的重视。然后找出应用程序,计算机,服务器和网络是否堵塞。


4
  1. 当他们在这些领域没有任何形式的正式灌输时,请以为他们是DBA和数据建模人员/设计人员。

  2. 认为他们的项目不需要DBA,因为这些东西都很容易/琐碎。

  3. 无法正确地区分应在数据库中完成的工作和应在应用程序中完成的工作。

  4. 不验证备份,或不备份。

  5. 在其代码中嵌入原始SQL。



3

不了解数据库并发模型及其对开发的影响。在事实之后添加索引和调整查询很容易。但是,设计时未适当考虑热点,资源争用和正确操作(假设您刚刚阅读的内容仍然有效!)的应用程序可能需要对数据库和应用程序层进行重大更改,以便以后进行更正。


3

不了解DBMS的工作原理。

如果不了解离合器的工作原理,就无法正确驾驶操纵杆。而且,如果您不了解您实际上只是在写硬盘上的文件,就无法理解如何使用数据库。

特别:

  1. 您知道什么是聚集索引吗?设计架构时您是否考虑过?

  2. 您知道如何正确使用索引吗?如何重用索引?您知道什么是覆盖指数吗?

  3. 太好了,您有了索引。索引中的1行有多大?当您拥有大量数据时,索引将有多大?那会很容易融入记忆吗?如果不是这样,它就没有用做索引。

  4. 您曾经在MySQL中使用EXPLAIN吗?大。现在对自己诚实:您是否了解所见内容的一半?不,您可能没有。解决这个。

  5. 您了解查询缓存吗?您知道什么使查询无法进行吗?

  6. 您正在使用MyISAM吗?如果您需要全文搜索,无论如何MyISAM都是垃圾。使用狮身人面像。然后切换到Inno。


2
一个更好的类比可能是,如果不了解离合器,就无法正确手动变速器进行故障排除。很多人在不知道离合器如何工作的情况下正确地进行了换挡。
迈克尔·复活节

3
  1. 使用ORM进行批量更新
  2. 选择比所需更多的数据。同样,通常在使用ORM时完成
  3. 循环触发SQL。
  4. 没有好的测试数据,仅注意到实时数据的性能下降。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.