22

以下のSQLクエリは非常にゆっくりと実行されています。実行計画を調べたところ、Files.OrderId の並べ替えが最もコストの高い操作 (53%) であると主張しています。OrderId で注文していない場合、なぜこれが起こるのでしょうか? File.OrderId にインデックスを作成するのが最善の策ですか?

誰かが興味を持っている場合の実行計画。

with custOrders as
(
    SELECT c.firstName + ' ' + c.lastname as Customer, c.PartnerId , c.CustomerId,o.OrderId,o.CreateDate, c.IsPrimary
    FROM Customers c
    LEFT JOIN CustomerRelationships as cr
        ON c.CustomerId = cr.PrimaryCustomerId
    INNER JOIN Orders as o
       ON c.customerid = o.customerid 
           OR (cr.secondarycustomerid IS NOT NULL AND o.customerid = cr.secondarycustomerid)
    where c.createdate >= @FromDate + ' 00:00' 
       AND c.createdate <= @ToDate + ' 23:59' 
),
 temp as
(
SELECT Row_number() 
         OVER ( 
           ORDER BY c.createdate DESC)                    AS 'row_number', 
       c.customerid as customerId, 
       c.partnerid as partnerId, 
       c.Customer, 
       c.orderid as OrderId, 
       c.createdate as CreateDate, 
       Count(f.orderid)                                   AS FileCount, 
       dbo.Getparentcustomerid(c.isprimary, c.customerid) AS ParentCustomerId, 
       au.firstname + ' ' + au.lastname                   AS Admin, 
       '' as blank, 
       0  as zero
FROM   custOrders c 
       INNER JOIN files f 
               ON c.orderid = f.orderid 
       INNER JOIN admincustomers ac 
               ON c.customerid = ac.customerid 
       INNER JOIN adminusers au 
               ON ac.adminuserid = au.id 
       INNER JOIN filestatuses s 
               ON f.statusid = s.statusid 
WHERE  ac.adminuserid IS NOT NULL 
       AND f.statusid NOT IN ( 5, 6 ) 
GROUP  BY c.customerid, 
          c.partnerid, 
          c.Customer, 
          c.isprimary, 
          c.orderid, 
          c.createdate, 
          au.firstname, 
          au.lastname 
)
4

4 に答える 4

16

SQL Server には、2 つのテーブルを結合する必要がある場合に選択できる 3 つのアルゴリズムがあります。Nested-Loops-Join、Hash-Join、Sort-Merge-Join。どちらを選択するかは、コスト見積もりに基づいています。この場合、利用可能な情報に基づいて、Sort-Merge-Join が正しい選択であると判断しました。

SQL Server の実行計画では、Sort-Merge は、Sort と Merge-Join の 2 つの演算子に分割されます。これは、データが既に並べ替えられている場合など、並べ替え操作が必要ない場合があるためです。

結合の詳細については、こちらの結合シリーズをご覧ください: http://sqlity.net/en/1146/a-join-a-day-introduction/ Sort-Merg-Join に関する記事はこちら: http:// sqlity.net/en/1480/a-join-a-day-the-sort-merge-join/


クエリを高速化するために、まずインデックスを調べます。クエリに一連のクラスター化インデックス スキャンがあります。それらのいくつかをシークに置き換えることができれば、おそらくより良いでしょう。また、SQL Server が生成する見積もりが、実際の実行計画の実際の行数と一致するかどうかも確認してください。それらが大きく離れている場合、SQL Server は多くの場合、不適切な選択をします。したがって、より良い統計を提供すると、クエリのパフォーマンスも向上します。

于 2013-01-08T16:41:34.417 に答える
3

SQL Server は並べ替えを実行して、その並べ替え演算子の右側にあるデータセットとOrdersテーブル内のレコードとの間のマージ結合を有効にします。マージ結合自体は、データセット内のすべてのレコードを結合する非常に効率的な方法ですが、結合する各データセットを結合キーに従って同じ順序で並べ替える必要があります。

PK_Ordersキーは既にによって並べ替えられているためOrderID、SQL Server は結合のもう一方の端 (並べ替えの右側にあるその他のもの) を並べ替えることでそれを利用することを決定し、計画のその時点で 2 つのデータセットをマージできるようにしました。 . マージ結合の一般的な代替手段はハッシュ結合ですが、並べ替えとマージの代わりに高価なハッシュ結合演算子を使用するため、これは役に立ちません。クエリ オプティマイザーは、この場合、並べ替えとマージがより効率的であると判断しました。

計画の高価なステップの根本的な原因は、注文テーブルのすべてのレコードをデータセットに結合する必要があることです。files テーブルからのレコードを制限する方法はありますか? files.statusid5,6 にないレコードがテーブルの合計サイズの 10% 未満である場合は、インデックスが役立つ場合があります。

QO は、ほとんどのレコードが最後に除外されると考えています。計画の途中で処理する必要のあるレコードが少なくなるように、これらのフィルター条件をできるだけ多くレコード ソースにプッシュするようにしてください。

編集:言及するのを忘れていましたが、確認できる実行計画があると非常に役立ちます。実際の実行計画の結果を取得して、これらの演算子を通過するレコードの実際の数を確認する方法はありますか? 推定レコード数が少しずれることがあります。

編集:最後から2番目のフィルター演算子の述語フィールドをさらに詳しく見て、要約します:

c.CustomerId=o.CustomerId
OR o.CustomerId=cr.SecondaryCustomerId AND cr.SecondaryCustomerId IS NOT NULL

SQL Server は、クエリ内のこの時点までの一致する可能性のあるすべてのレコード (最後から 2 番目のフィルター演算子の右側の計画) の間でクロス結合を生成し、その条件で各レコードを調べてOrdersCustomers確かに一致します。フィルタに入るラインが非常に太く、出てくるラインが非常に細いことに注目してください。これは、その演算子の後、推定行数が 21k から 4 になるためです。前に言ったことは忘れてください。おそらくこれが計画の主な問題です。これらの列にインデックスがあっても、結合条件が複雑すぎるため、SQL Server はそれらを使用できません。完全な結合述語をすぐに使用できないため、必要なレコードだけを探すのではなく、すべてのレコードを一緒にマージする計画が発生します。

私が最初に考えたのは、CTE を 2 つのデータセットの結合として言い換えることcustOrdersです。これにより、残りの CTE の作業が複製されますが、インデックスを適切に使用できるようになれば、大きな成果が得られる可能性があります。CustomerIdSecondaryCustomerId

于 2013-01-08T16:49:02.437 に答える
1

この結合で並べ替えが発生していると思います。

FROM   custOrders c 
       INNER JOIN files f 
               ON c.orderid = f.orderid 

クエリは statusid 列も使用するため、orderid 列と statusid 列を含むファイルにインデックスを作成します。

次の変更を検討することもできます。

  1. これは adminusers と admincustomers の間の内部結合によってカバーされるため、「ac.adminuserid IS NOT NULL」は必要ありません。
  2. テスト "f.statusid NOT IN ( 5, 6 )" を肯定的な条件 (In など) に変更します。否定的な条件は処理にコストがかかるためです。
于 2013-01-08T16:40:51.877 に答える