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

SQL Anywhere 12.0.1 » 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 句にテーブルを多くても 1 つだけ含んだ相関サブクエリに対して常に実行されます。これらの相関サブクエリは、ANY、ALL、EXISTS の各述部で使用されます。クエリのセマンティクス上、サブクエリが返すローは多くても 1 つだけであると判断できる場合は、非相関サブクエリ、または FROM 句に複数のテーブルを含んだサブクエリはフラットにされます。

この例では、サブクエリは外部ブロック内の各ローに対して、多くても 1 つのローしか一致させられません。多くても 1 つのローしか一致させられないため、内部ジョインに変換できます。

サブクエリのネスト解除

改善前:

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;

サブクエリで、外部ブロックの各ローに対して多くても 1 つのローしか一致しない場合、比較しているサブクエリを削除します。

インデックスカラムを問い合わせる場合の IN 述部の使用の検討

改善前:

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

改善後:

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

書き換えられた形式では、IN リスト述部を検索引数可能として扱い、インデックス検索に利用できます。また、オプティマイザーが IN リストをインデックスのソート順に合わせてソートし、検索効率を向上させることができます。

定数だけ、またはクエリブロック 1 回の実行中に定数である値 (外部参照など) だけを 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;

次の場合にジョインの排除を検討してください。

  • ジョインがプライマリキーから外部キーへのジョインで、プライマリテーブルからのプライマリキーのカラムだけがクエリで参照される場合。この場合は、プライマリキーテーブルが更新可能でないと、削除されます。

  • ジョインが同じテーブルの 2 つのインスタンス間におけるプライマリキーからプライマリキーへのジョインである場合。この場合は、一方のテーブルが更新可能でないと、そのテーブルは削除されます。

  • ジョインが外部ジョインであり、NULL 入力テーブル式は、外部ジョインの保護された側の各ローについて、多くても 1 つのローしか返さず、NULL 入力テーブル式で生成される式は、外部ジョイン以降のクエリの残りの部分で必要ない場合。

この場合、ジョインはプライマリキーから外部キーへのジョインであり、プライマリキーテーブル Products は削除できます。つまり、2 番目のクエリは、SalesOrderItems テーブルの中で、Products への NULL 外部キーを持つローが結果に現れないため、セマンティック上は 1 番目のクエリと同じです。

不要なジョインの排除

改善前:

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;

最初のクエリでは、NULL 入力テーブル式が保護された側のローに対して複数のローを生成できず、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 文を 1 つ含んでいる

  • 1 つの変数を宣言し、その変数に代入して 1 つの値を返す

  • 1 つの変数を宣言し、その変数に SELECT INTO して 1 つの値を返す

このヒントは、テンポラリ関数、再帰関数、または NOT DETERMINISTIC 句のある関数には適用されません。

また、このヒントは関数がサブクエリを使用して引数として呼び出された場合、または関数がテンポラリプロシージャーの内部から呼び出された場合にも適用されません。

単純なストアドプロシージャーのインライン化の検討

改善前:

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;

単一の SELECT 文としてのみ定義されたストアドプロシージャーは、クエリの FROM 句で呼び出すときにインライン化できます。プロシージャーがインライン化される場合は、派生テーブルとして書き換えられます。このヒントは、デフォルトの引数を使用し、単一の SELECT 文以外が本文に存在するプロシージャーには適用されません。

 参照