Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SAP Sybase SQL Anywhere 16.0 (中文) » SQL Anywhere 服务器 - SQL 用法 » 性能提高、诊断和监控 » 关于提高性能的提示

 

提示:建立高效的 SQL 查询

要提高查询处理性能,可考虑使用以下提示来建立更加高效的查询。这些提示反映了优化程序为了更加高效地重写查询而会在查询处理过程中选择的优化方法。通过在查询中构建这些高效方法,优化程序可能会减少很多工作量。

提示 之前和之后 解释

消除不必要的 DISTINCT 条件

之前:

SELECT DISTINCT p.ID, p.Quantity
FROM Products p;

之后:

SELECT p.ID, p.Quantity
FROM Products p;

由于 Products 表包含主键 p.ID(它是结果集的一部分),因此第一个语句中的 DISTINCT 关键字是不必要的。

消除不必要的 DISTINCT 条件

之前:

SELECT DISTINCT *
FROM SalesOrders o JOIN Customers c
   ON o.CustomerID = c.ID
WHERE c.State = 'NY';

之后:

SELECT  *
FROM SalesOrders o JOIN Customers c
   ON o.CustomerID = c.ID
WHERE c.State = 'NY';

第一个查询包含两个表的主键,因此结果中的每一行肯定是不同的。

取消子查询嵌套

之前:

SELECT s.*
FROM SalesOrderItems s
WHERE EXISTS ( SELECT *
 FROM Products p
 WHERE s.ProductID = p.ID
  AND p.ID = 300 
  AND p.Quantity > 20);

之后:

SELECT s.*
FROM Products p JOIN SalesOrderItems s
   ON p.ID = s.ProductID
WHERE p.ID = 300 AND p.Quantity > 20;

将嵌套查询重写为连接通常会提高执行和优化的效率。一般情况下,会始终对 FROM 子句中最多包含一个表的相关子查询(用于 ANY、ALL 和 EXISTS 谓语中)执行取消子查询嵌套。如果根据查询语义可以确定子查询最多返回一行,则不相关子查询或在 FROM 子句中有多个表的子查询将被展平。

在本示例中,对于外部块中的每个行,子查询最多可以匹配一行。因为子查询最多可以匹配一行,因此可将其转换为内连接。

取消子查询嵌套

之前:

SELECT p.*
FROM Products p
WHERE EXISTS
   ( SELECT *
     FROM SalesOrderItems s
     WHERE s.ProductID = p.ID
       AND s.ID = 2001);

之后:

SELECT DISTINCT p.*
FROM Products p JOIN SalesOrderItems s
   ON p.ID = s.ProductID
WHERE s.ID = 2001;

“之前”查询在子查询中包含一个连接性 EXISTS 谓语,可以匹配多个行。在 SELECT 列表中使用 DISTINCT 可将该查询转换为内连接。

取消子查询嵌套

之前:

SELECT *
FROM Products p
WHERE p.ID =
    ( SELECT s.ProductID
      FROM SalesOrderItems s
      WHERE s.ID = 2001
         AND s.LineID = 1 );

之后:

SELECT p.*
FROM Products p, SalesOrderItems s
WHERE p.ID = s.ProductID
   AND s.ID = 2001
   AND s.LineID = 1;

对于外部块中的每一行,如果该子查询最多可以匹配一行,则可在比较中消除子查询。

查询索引列时考虑使用 IN 谓语

之前:

SELECT *
FROM SalesOrders
WHERE SalesRepresentative = 902 
   OR SalesRepresentative = 195;

之后:

SELECT *
FROM SalesOrders
WHERE SalesRepresentative IN ( 195, 902 );

在重写形式中,可以将 IN 列表谓语当作可优化搜索谓语并利用它来进行索引检索。此外,优化程序可以对 IN 列表排序,以匹配索引的排序序列,从而使检索更为有效。

IN 列表必须只包含常量或在查询块的一次执行中为常量的值(例如外部引用)。

消除不必要的连接

之前:

SELECT s.ID, s.LineID, p.ID
FROM SalesOrderItems s KEY JOIN Products p
FOR READ ONLY;

之后:

SELECT s.ID, s.LineID, s.ProductID
FROM SalesOrderItems s
WHERE s.ProductID IS NOT NULL
FOR READ ONLY;

在以下情况下考虑消除连接:

  • 连接属于主键到外键连接,且在查询中只引用主表中的主键列。在这种情况下,如果主键表是不可更新的,则它会被排除。

  • 连接属于同一表的两个实例之间的主键到主键连接。在这种情况下,如果其中一个表是不可更新的,则它会被排除。

  • 连接属于外连接。对于外连接的保留一侧的每一行,提供空值的表表达式最多返回一行。在外连接以外的其余查询中,不需要任何由提供空值的表表达式所生成的表达式。

在本例中,连接属于主键到外键的连接,因此可消除主键表 Products。也就是说,由于 SalesOrderItems 表中具有引用 Products 表的 NULL 外键的任何行都不会出现在结果中,因此第二个查询在语义上与第一个查询等效。

消除不必要的连接

之前:

SELECT s.ID, s.LineID
FROM SalesOrderItems s 
 LEFT OUTER JOIN Products p 
   ON p.ID = s.ProductID
WHERE s.Quantity > 5 
FOR READ ONLY;

之后:

SELECT s.ID, s.LineID
FROM SalesOrderItems s  
WHERE s.Quantity > 5 
FOR READ ONLY;

在第一个查询中,因为提供空值的表表达式不能为保留一侧的任何行生成多个行,且在 LEFT OUTER JOIN 上没有使用 Products 表中的任何列,所以可以消除 OUTER JOIN。

消除不必要的大小写转换

之前:

SELECT *
FROM Customers
WHERE UPPER(Surname) = 'SMITH';

之后:

SELECT *
FROM Customers
WHERE Surname = 'SMITH';

对于不区分大小写的数据库,可以重写第一个查询,以便优化程序可以考虑使用 Customers.Surname 上的索引。

缺省情况下,数据库服务器执行不区分大小写的字符串比较操作,除非显式指定了文本转换说明(使用 UPPER、UCASE、LOWER、LCASE)。消除不必要的大小写转换可将谓语转换为可优化搜索谓语,以便用于相应表的索引检索。

考虑内置函数

之前:

CREATE FUNCTION F1( arg1 INT, arg2 INT )
RETURNS INT
BEGIN
 RETURN arg1 * arg2
END;
SELECT F1( e.EmployeeID, 2.5 ) 
FROM Employees e;

之后:

SELECT CAST( e.EmployeeID AS INT ) * CAST( 2.5 AS INT ) 
FROM Employees e;

如果用户定义的函数采用以下形式之一,则可以内置这些函数:

  • 包含一个 RETURN 语句

  • 声明一个变量、为该变量赋值并返回一个值

  • 声明一个变量、选择到该变量中并返回一个值

此提示不适用于临时函数、递归函数或包含 NOT 子句的函数。

此提示也不适用于下面的情况:通过子查询将函数作为参数调用或从临时过程内部调用函数。

考虑内置简单的存储过程

之前:

CREATE PROCEDURE Test1( arg1 INT )
 BEGIN
  SELECT * FROM Employees WHERE EmployeeID=arg1
 END;
SELECT * FROM Test1( 200 );

之后:

SELECT * FROM ( 
      SELECT * FROM Employees 
      WHERE EmployeeID=CAST( 200 AS INT ) ) 
   AS Test1;

在查询的 FROM 子句中调用某个仅被定义为单一 SELECT 语句的存储过程时,可以内置该存储过程。内置该过程时,它会被重写为派生表。此提示不适用于使用缺省参数以及在主体中包含单一 SELECT 语句之外的其它语句的过程。

 另请参见