SQL 是开发和查询数据库的主要语言,但它有一些怪癖。在我的上一篇文章中,我分享了 7 个需要避免的 SQL 错误。现在,让我们来看看编写更快的 SQL 查询的 9 个最佳实践。
更快 SQL 查询的 9 个最佳实践
-
仅检索您需要的列
-
使用 CASE 而不是 UPDATE 进行条件列更新
-
将大表查询保持在最低限度
-
预先准备您的数据
-
批量执行删除和更新
-
使用临时表提高游标性能
-
使用表值函数而不是标量函数
-
使用分区来避免大量数据移动
-
使用存储过程来提高性能,使用 ORM 来提高便利性
仅检索您需要的列
一个常见的 SQL 习惯是在查询上使用SELECT *,因为列出所需的所有列是很乏味的。另外,有时这些列可能会随着时间的推移而改变,所以为什么不以简单的方式做事呢?
但是,如果您查询具有一百个或更多列的表上的所有列,会发生什么情况?这些庞然大物在野外以令人沮丧的规律性出现,并且并不总是能够将它们重新设计为更理智的模式。有时,驯服这头野兽的唯一方法是选择列的子集,这可以防止其他查询资源匮乏。
在对查询进行原型设计时使用是可以的SELECT *,但是任何进入生产的内容都应该只请求实际使用的列。
使用 CASE 而不是 UPDATE 进行条件列更新
开发人员经常做的其他事情是UPDATE ... WHERE根据另一列的值设置一列的值,例如UPDATE Users SET Users.Status="Legacy" WHERE Users.ID<1000. 这种方法简单直观,但有时会增加不必要的步骤。
例如,如果您将数据插入表中,然后使用UPDATE它来更改它,就像我刚才所示,那么这是两个单独的事务。当您有数百万行时,额外的事务可能会创建许多不必要的操作。
对于如此大规模的操作,更好的解决方案是在插入操作本身期间CASE,在查询中使用内联语句来设置列值。这样,您可以在一次传递中处理初始插入和修改的数据。
将大表查询保持在最低限度
对任何大小的表的查询都不是免费的。对数亿或数十亿行的表进行查询绝对不是免费的。
只要有可能,将对大表的查询合并为尽可能少的离散操作。例如,如果您有一个表,您想要先按一列查询,然后再按另一列查询,请首先将其合并到单个查询中,然后确保您要查询的列具有覆盖索引。
如果您发现自己从大表中获取相同的数据子集并对其运行较小的查询,则可以通过在其他地方保留该子集并对其进行查询来为自己和其他人加快速度。这引出了下一个技巧。
预先准备您的数据
假设您或组织中的其他人经常运行需要通过连接多个大型表来聚合大量数据的报告或存储过程。您不必每次都重新运行联接,而是可以通过将其"预先暂存"到专门用于此目的的表中来为自己(和其他人)节省大量工作。然后,报告或过程可以针对该表运行,因此它们共同的工作只需完成一次。如果您有足够的资源,并且您的数据库支持它,您可以使用内存表来进一步加快速度。
批量执行删除和更新
想象一个拥有数十亿行的表,需要从中清除数百万行。简单的方法是简单地DELETE在事务中运行 a 。但随后整个表将被锁定,直到事务完成。
更复杂的方法是批量执行删除(或更新)操作,可以与其他操作交错。每个事务变得更小并且更易于管理,并且其他工作可以在操作周围和操作期间进行。
在应用程序方面,这是任务队列的一个很好的用例,它可以跟踪跨会话的操作进度,并允许它们作为低优先级后台操作执行。
使用临时表提高游标性能
在大多数情况下,应该避免使用游标------它们很慢,它们会阻止其他操作,而且它们完成的任何事情几乎总是可以通过其他方式完成。不过,如果您因某种原因而无法使用游标,临时表可以减少随之而来的性能问题。
例如,如果您需要循环遍历表并根据某些计算更改列,您可以获取要更新的候选数据,将其放入临时表中,使用游标循环遍历该表,然后应用所有在单个操作中更新。您还可以通过这种方式将游标处理分解为批次。
使用表值函数而不是标量函数
标量函数可让您将计算封装到类似存储过程的 SQL 片段中。将标量函数的结果作为SELECT查询中的列返回是常见的做法。
如果您发现自己在 Microsoft SQL Server 中经常这样做,则可以通过使用表值函数并 CROSS APPLY 在查询中使用来获得更好的性能。有关很少讨论的APPLY运算符的更多信息,请参阅Microsoft Virtual Academy 的此培训模块。
使用分区来避免大量数据移动
SQL Server Enterprise 提供"分区"功能,允许您将数据库表拆分为多个分区。如果您经常将一个表归档到另一个表中,则可以避免使用它INSERT/DELETE来移动数据,而是使用它SWITCH。
例如,如果您有一个每天清空到存档表中的表,则可以通过SWITCH简单地将每日表中的页面分配给存档表来执行此清空和复制操作。切换过程所需的时间比手动复制和删除要少几个数量级。Cathrine Wilhelmsen 有一个关于如何以这种方式使用分区的优秀教程。
使用存储过程来提高性能,使用 ORM 来提高便利性
ORMS(对象关系映射器)是生成以编程方式生成的 SQL 代码的软件工具包。它们允许您使用应用程序的编程语言及其隐喻来开发和维护您的查询。
许多数据库开发人员原则上不喜欢 ORM。它们因生成低效且有时无法优化的代码而臭名昭著,并且它们使开发人员学习 SQL 和了解其查询在做什么的动力较少。当开发人员需要手动编写查询以获得最佳性能时,他们不知道如何操作。
另一方面,ORM 使编写和维护数据库代码变得更加容易。应用程序的数据库部分并没有位于另一个域的某个地方,并且它的编写方式与应用程序逻辑的耦合更加松散。
对于经常调用、需要良好性能、不太可能经常更改(如果有的话)并且需要通过数据库分析工具调查性能的查询,使用存储过程是最有意义的。与即席查询相比,大多数数据库都可以更轻松地为存储过程获取此类统计信息。数据库的查询规划器也更容易优化存储过程。
将更多数据库逻辑移至存储过程的缺点是逻辑与数据库的耦合更加紧密。存储过程可能会从性能优势转变为巨大的技术债务。如果您决定稍后迁移到另一种数据库技术,则更改 ORM 的目标比重写所有存储过程更容易。此外,还可以检查 ORM 生成的代码以进行优化,并且查询缓存通常允许重用最常生成的查询。
如果应用程序端的可维护性很重要,请使用 ORM。如果是数据库端性能,就使用存储过程。
作者:Serdar Yegulalp
更多技术干货请关注公号【云原生数据库】
squids.cn,云数据库RDS,迁移工具DBMotion,云备份DBTwin等数据库生态工具。
irds.cn,多数据库管理平台(私有云)。