📜  编写 SQL 查询的最佳方法是什么?

📅  最后修改于: 2021-10-21 05:14:46             🧑  作者: Mango

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

现在我们已经分析了表CustomersProductsOrders ,下面给出了优化查询的不同方法,以及来自这些表的查询示例:

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 与优化查询所需的特定字段一起使用。

示例:这是一个查询,当只需要CustomerIDLastName时,它会显示表 Customers 中的所有数据。

SELECT * 
FROM Customers;

最好使用带有CustomerIDLastName字段的 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%'