SQL查询用于从数据库中检索所需的数据。但是,可能有多个 SQL 查询产生相同的结果,但效率不同。低效的查询会耗尽数据库资源、降低数据库速度或导致其他用户无法提供服务。所以优化查询以获得最佳的数据库性能是非常重要的。
让我们考虑一些示例表来了解这些优化查询的不同方法。
客户:表客户包含商店的潜在客户的详细信息。
CustomerID | LastName | FirstName | Address | Age |
---|---|---|---|---|
73001 | Smith | John | 45 Jump Street | 21 |
73002 | Parker | Anna | 83 Wild Avenue | 45 |
73003 | James | Josie | 99 Chestnut Avenue | 25 |
73004 | White | Anna | 55 Paper Street | 72 |
73005 | Sparks | Harry | 11 Wisteria Lane | 23 |
73006 | Parker | Jane | 12 Quentin Road | 50 |
产品:表产品包含商店中可用产品的详细信息。
ProductID | ProductName | ProductPrice |
---|---|---|
1001 | Shampoo | 100 |
1002 | Tooth paste | 20 |
1003 | Soap | 15 |
1004 | Hand Sanitizer | 50 |
1005 | Deodorant | 100 |
订单:表订单包含客户从商店订购的产品的详细信息。
CustomerID | ProductID | ProductQuantity |
---|---|---|
73001 | 1003 | 5 |
73001 | 1001 | 1 |
73003 | 1002 | 1 |
73004 | 1003 | 2 |
73004 | 1005 | 1 |
现在我们已经分析了表Customers 、 Products和Orders ,下面给出了优化查询的不同方法,以及来自这些表的查询示例:
1. 为查询提供正确的格式
在编写查询时提供正确的格式非常重要。这增强了查询的可读性,也使审查和故障排除更容易。下面给出了一些格式化查询的规则:
- 将查询中的每个语句放在一个新行中。
- 将 SQL 关键字以大写形式放入查询中。
- 在查询中使用 CamelCase 大写并避免下划线(写入 ProductName 而不是 Product_Name)。
示例:这是一个查询,显示当前已订购产品且年龄小于 50 岁的客户的 CustomerID 和 LastName。
Select distinct Customers.CustomerID, Customers.LastName from Customers INNER join Orders on Customers.CustomerID = Orders.CustomerID where Customers.Age < 50;
上面的查询看起来不可读,因为所有语句都在一行中并且关键字都是小写的。因此,下面给出了使用先前指定的格式规则的优化版本。
SELECT DISTINCT Customers.CustomerID, Customers.LastName
FROM Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
WHERE Customers.Age < 50;
2. 指定 SELECT 字段而不是使用 SELECT *
SELECT *用于从表中获取所有数据。因此,除非给定条件实际上需要所有数据,否则不应使用它,因为它非常低效并且会减慢查询的执行时间。最好将 SELECT 与优化查询所需的特定字段一起使用。
示例:这是一个查询,当只需要CustomerID和LastName时,它会显示表 Customers 中的所有数据。
SELECT *
FROM Customers;
最好使用带有CustomerID和LastName字段的 select 语句来获得所需的结果。
SELECT CustomerID, LastName
FROM Customers;
3. 如果不需要,删除相关的子查询
相关子查询是一个嵌套查询,它的值依赖于外部查询。如果数据库中有数百万用户,相关子查询效率低下并且需要大量时间,因为它需要运行数百万次。在这种情况下,内部联接更有效。
示例:这是一个使用相关子查询显示当前已订购产品的客户的CustomerID的查询。
SELECT CustomerID
FROM Customers
WHERE EXISTS (SELECT * FROM Orders
WHERE Customers.CustomerID = Orders.CustomerID);
在这种情况下最好使用内连接来获得相同的结果。
SELECT DISTINCT Customers.CustomerID
FROM Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;
注意:如果数据库中几乎所有行都需要,最好避免相关子查询。但是,在某些情况下,它们是不可避免的,必须使用。
4.限制查询得到的结果
如果只需要有限的结果,最好使用 LIMIT 语句。此语句限制记录并且仅显示指定的记录数。例如:如果有一个包含百万条记录的大型数据库,并且只需要前十条记录,则最好使用 LIMIT 语句,因为这将确保只获取相关记录,而不会使系统负担过重。
示例:这是一个显示限制为 3 的客户详细信息的查询:
SELECT *
FROM Customers
LIMIT 3;
5. 如果不需要,删除 DISTINCT 子句
DISTINCT 子句用于通过消除重复项从查询中获得不同的结果。但是,这会增加查询的执行时间,因为所有重复字段都组合在一起。因此,最好尽可能避免使用 DISTINCT 子句。作为替代方法,可以使用 GROUP BY 子句来获得不同的结果。
示例:这是一个使用 DISTINCT 子句显示所有客户的不同姓氏的查询。
select distinct LastName
from Customers;
还可以使用 GROUP BY 子句获取客户的不同姓氏,下一个示例演示了该子句:
SELECT LastName
FROM CUSTOMERS
GROUP BY LastName;
6. 避免在谓词中使用函数
SQL 中的函数用于执行特定操作。然而,它们效率很低,因为它们不允许使用索引,这反过来会减慢查询的执行时间。所以最好尽量避免查询中的函数,以确保其优化。
示例:这是一个查询,显示名称以“ Sha ”开头的产品的详细信息。
SELECT *
FROM Products
WHERE SUBSTR(ProductName, 1, 3) = 'Sha';
最好避免使用该函数并改用 LIKE 子句以获得相同的结果。
SELECT *
FROM Products
WHERE ProductName LIKE 'Sha%';
7. 尽可能避免使用 OR、AND、NOT运算符
当使用 OR、AND、NOT运算符时,很有可能没有使用索引。在大型数据库的情况下,最好为这些找到替代品以加快查询的执行时间。
OR 和 AND运算符的示例如下:
示例 1:这是一个查询,它使用 OR运算符显示 CustomerID 为 73001、73004 和 73005 的客户的详细信息。
SELECT *
FROM Customers
WHERE CustomerID = 73001
OR CustomerID = 73004
OR CustomerID = 73005;
在这种情况下最好使用 IN运算符来获得相同的结果。
SELECT *
FROM Customers
WHERE CustomerID IN (73001, 73004, 73005);
示例 2:这是一个使用 AND运算符显示年龄在 25 到 50 岁之间的客户的详细信息的查询。
SELECT *
FROM Customers
WHERE age >= 25 AND age <= 50;
在这种情况下最好使用 BETWEEN运算符来获得相同的结果。
SELECT *
FROM Customers
WHERE age BETWEEN 25 AND 50;
8. 尽可能使用 WHERE 子句而不是 HAVING 子句
HAVING 子句与 GROUP BY 子句一起使用以强制执行条件,因为 WHERE 子句不能与聚合函数一起使用。但是,HAVING 子句不允许使用会减慢查询执行时间的索引。因此,最好尽可能使用 WHERE 子句而不是 HAVING 子句。
示例:这是一个查询,显示 Customer FirstNames 以及年龄超过 25 岁的客户的客户数量。这是使用 HAVING 子句完成的。
SELECT FirstName, COUNT(*)
FROM Customers
GROUP BY FirstName
HAVING Age > 25;
在这种情况下最好使用 WHERE 子句,因为它将条件应用于单个行,而不是将条件应用于 GROUP BY 子句的结果的 HAVING 子句。
SELECT FirstName, COUNT(*)
FROM Customers
where Age > 25
GROUP BY FirstName;
9. 使用 INNER JOIN 而不是 WHERE 子句来创建连接
使用 WHERE 子句创建连接会产生笛卡尔积,其中行数是两个表的行数的乘积。这对于大型数据库来说显然是有问题的,因为需要更多的数据库资源。所以最好使用 INNER JOIN,因为它只组合两个表中满足所需条件的行。
示例:这是一个使用 WHERE 子句显示当前已订购产品的客户的 CustomerID 的查询。
SELECT DISTINCT Customers.CustomerID
FROM Customers, Orders
WHERE Customers.CustomerID = Orders.CustomerID;
在这种情况下最好使用内连接来获得相同的结果。
SELECT DISTINCT Customers.CustomerID
FROM Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID;
10.避免字符在LIKE子句模式的开始
通配符,如%和_用于过滤掉LIKE子句的结果。但是,它们不应在模式的开头使用,因为这会禁用数据库使用索引。在这种情况下,需要进行全表扫描以匹配消耗更多数据库资源的模式。所以,最好是避免字符在模式的开始,仅在结束时,如果可能使用它们。
例子:
SELECT * FROM Customers
WHERE FirstName LIKE '%A%'
上述查询是低效的,因为它使用字符%在模式的开始。下面给出了避免这种情况的更有效的查询版本:
SELECT * FROM Customers
WHERE FirstName LIKE 'A%'