你应该避免的SQL反模式 Zachary Thomas 2025-10-31 0 浏览 0 点赞 长文 编写高效、可读且可维护的SQL是数据专业人士的一项核心技能。然而,许多开发者在日常工作中会不自觉地使用一些“反模式”——即那些看似可行但实际上会带来性能、可读性或维护性问题的坏习惯。本文将介绍几种最应该避免的SQL反模式。 ## 反模式一:滥用 `SELECT *` 这是最常见也最容易犯的错误。虽然在临时探索数据时很方便,但在生产代码中应绝对避免。 * **性能问题**: 它会强制数据库读取所有列的数据,即使你只需要其中几列。这增加了不必要的I/O、网络流量和内存消耗。更糟糕的是,它可能阻止数据库使用“覆盖索引”(covering index)这一强大的性能优化手段。 * **可读性差**: 代码的阅读者无法立即看出这个查询到底需要哪些数据。 * **脆弱性**: 如果底层表的结构发生变化(例如,增加或删除列),依赖 `SELECT *` 的代码可能会在不经意间中断。 **最佳实践**: 始终明确列出你所需要的每一列的名称。 ## 反模式二:在 `WHERE` 子句中使用隐式 JOIN 老式的SQL写法允许在 `FROM` 子句中列出多个表,然后在 `WHERE` 子句中指定它们的连接条件,例如 `FROM table1, table2 WHERE table1.id = table2.id`。 * **可读性差**: 连接逻辑和过滤逻辑混杂在 `WHERE` 子句中,难以区分。 * **易出错**: 如果忘记在 `WHERE` 中添加连接条件,查询不会报错,而是会执行一个“笛卡尔积”(Cross Join),返回的结果集将是两个表行数的乘积,这通常会导致灾难性的性能问题和错误结果。 **最佳实践**: 始终使用显式的 `JOIN` 语法(如 `INNER JOIN`, `LEFT JOIN`),并将连接条件放在 `ON` 子句中。 ## 反模式三:误用 `HAVING` 作为 `WHERE` `WHERE` 和 `HAVING` 都用于过滤数据,但它们的作用阶段完全不同。 * `WHERE` 子句在数据进行分组和聚合 **之前** 对行进行过滤。 * `HAVING` 子句在数据完成 `GROUP BY` 聚合 **之后** 对分组结果进行过滤。 如果在聚合前就可以过滤掉数据,却错误地使用了 `HAVING`,意味着数据库不得不对更多不必要的数据进行分组和聚合计算,严重影响性能。 **最佳实践**: 尽可能早地过滤数据。如果一个条件不依赖于聚合函数(如 `COUNT()`, `SUM()`, `AVG()`),就使用 `WHERE`。 ## 反模式四:在 `WHERE` 子句的列上使用函数 当你在 `WHERE` 子句中对一个列使用函数时(例如 `WHERE YEAR(order_date) = 2025`),你可能会阻止数据库优化器使用该列上的索引。这被称为“非SARGable”查询。优化器无法直接利用 `order_date` 列的索引来快速定位数据,而是被迫对表中的每一行都执行一次函数计算,即进行全表扫描。 **最佳实践**: 尽量将函数或计算操作应用在常量值上,而不是列上。例如,将上述查询改写为 `WHERE order_date >= '2025-01-01' AND order_date < '2026-01-01'`,这样索引就能被有效利用。 ## 反模式五:错误的数据类型选择 一个常见的错误是用字符串类型(如 `VARCHAR`)来存储日期或数字。 * **性能问题**: 需要在使用时进行显式或隐式类型转换,增加了CPU开销,且无法有效利用索引。 * **数据完整性**: 无法保证存入的数据格式总是正确的(例如,可能存入 "Not Applicable" 而不是一个日期)。 * **排序和比较错误**: 字符串的排序规则(按字典序)和日期、数字的排序规则完全不同,会导致错误的结果(例如,"10" 会排在 "2" 的前面)。 **最佳实践**: 为你的数据选择最合适、最具体的数据类型。用 `DATE` 或 `TIMESTAMP` 存储日期,用 `INTEGER` 或 `DECIMAL` 存储数字。 阅读 Data Methods 原文 本文的原始来源。 #SQL #反模式 #数据库 #最佳实践