应用程序开发人员常见的数据库开发错误有哪些?
当前回答
我讨厌开发人员使用嵌套的选择语句,甚至在查询的“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和内存使用。
其他回答
这里有一个视频链接,名为“经典数据库开发错误和克服它们的五种方法”,作者是Scott Walz
相关子查询导致的性能差
大多数情况下,您希望避免相关子查询。如果子查询中存在对外部查询的列的引用,则子查询是相关的。当发生这种情况时,对于返回的每一行至少执行一次子查询,如果在应用包含相关子查询的条件之后应用其他条件,则可以执行更多次。
请原谅这个不自然的示例和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
In this example, the query in the from clause is now an inline-view (again some Oracle specific syntax) and is only executed once. Depending on your data model, this query will probably execute much faster. It would perform better than the first query as the number of employees grew. The first query could actually perform better if there were few employees and many stores (and perhaps many of stores had no employees) and the daily_sales table was indexed on store_id. This is not a likely scenario but shows how a correlated query could possibly perform better than an alternative.
我曾多次看到初级开发人员关联子查询,这通常会对性能产生严重影响。但是,当删除一个相关的子查询时,一定要查看之前和之后的解释计划,以确保您没有使性能变差。
过度使用和/或依赖存储过程。
一些应用程序开发人员将存储过程视为中间层/前端代码的直接扩展。这似乎是微软堆栈开发人员的一个共同特征(我是其中之一,但我已经不再这样做了),并产生许多执行复杂业务逻辑和工作流处理的存储过程。这在其他地方做得更好。
存储过程在某些实际技术因素(例如性能和安全性)需要使用时非常有用。例如,保持大型数据集的聚合/过滤“接近数据”。
我最近不得不帮助维护和增强一个大型Delphi桌面应用程序,其中70%的业务逻辑和规则是在1400个SQL Server存储过程中实现的(其余在UI事件处理程序中)。这是一场噩梦,主要是由于难以将有效的单元测试引入TSQL,缺乏封装和糟糕的工具(调试器,编辑器)。
在过去与Java团队一起工作时,我很快发现在那个环境中,情况往往完全相反。一位Java架构师曾经告诉我:“数据库是用于数据的,而不是用于代码的。”
现在,我认为完全不考虑存储过程是错误的,但在它们提供有用好处的情况下,应该谨慎使用(不是默认情况)(参见其他答案)。
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,使您的程序员同事能够读懂。
在修复生产数据库内部的一些问题之前没有进行备份。 在存储过程中的存储对象(如表、视图)上使用DDL命令。 害怕使用存储的proc或害怕在更有效/更合适的地方使用ORM查询。 忽略数据库分析器的使用,它可以准确地告诉您ORM查询最终转换为什么,从而验证逻辑,甚至在不使用ORM时进行调试。
推荐文章
- 获得PostgreSQL数据库中当前连接数的正确查询
- MySQL数据库表中的最大记录数
- 从现有模式生成表关系图(SQL Server)
- HyperLogLog算法是如何工作的?
- 数据库和模式的区别
- 如何从命令行在windows中找到mysql数据目录
- 货币应该使用哪种数据类型?
- 如何找到MySQL的根密码
- 将表从一个数据库复制到另一个数据库的最简单方法?
- 什么是分片,为什么它很重要?
- 数据库触发器是必要的吗?
- 为什么我应该使用基于文档的数据库而不是关系数据库?
- 哪个更快/最好?SELECT *或SELECT columnn1, colum2, column3等
- 将值从同一表中的一列复制到另一列
- 什么是数据库池?
