1

プログラムで構築されたより大きなselectステートメント内のサブクエリであるselectステートメントがあります。問題は、このサブクエリを含めることを選択すると、ボトルネックとして機能し、クエリ全体が非常に遅くなることです。

データの例は次のとおりです。

Payment
.Receipt_no|.Person |.Payment_date|.Type|.Reversed| 
          2|John    |01/02/2001   |PA   |         |
          1|John    |01/02/2001   |GX   |         |
          3|David   |15/04/2003   |PA   |         |
          6|Mike    |26/07/2002   |PA   |R        |
          5|John    |01/01/2001   |PA   |         |
          4|Mike    |13/05/2000   |GX   |         |
          8|Mike    |27/11/2004   |PA   |         |
          7|David   |05/12/2003   |PA   |R        |
          9|David   |15/04/2003   |PA   |         |

サブクエリは次のとおりです。

select Payment.Person, 
Payment.amount 
from Payment
inner join (Select min([min_Receipt].Person) 'Person',
   min([min_Receipt].Receipt_no) 'Receipt_no' 
   from Payment [min_Receipt] 
   inner join (select min(Person) 'Person', 
      min(Payment_date) 'Payment_date' 
      from Payment
      where Payment.reversed != 'R' and Payment.Type != 'GX' 
      group by Payment.Person) [min_date] 
   on [min_date].Person= [min_Receipt].Person and [min_date].Payment_date = [min_Receipt].Payment_date 
   where [min_Receipt].reversed != 'R' and [min_Receipt].Type != 'GX' 
   group by [min_Receipt].Person) [1stPayment] 
on [1stPayment].Receipt_no = Payment.Receipt_no

これは、.Payment_date (昇順)、.Receipt_no (昇順) で各人の最初の支払いを取得します。ここで、.type は「GX」ではなく、.Reversed は「R」ではありません。次のように:

Payment
.Receipt_No|.Person|.Payment_date
          5|John   |01/01/2001
          3|David  |15/04/2003
          8|Mike   |27/11/2004

Ahmadsの投稿に続いて -

以下の結果から

(3|David  |15/04/2003) 
and (9|David  |15/04/2003)

私は、最低のレシート番号を持つレコードだけが欲しいです。そう

(3|David  |15/04/2003)  

そこで、集計関数 'min(Payment.receipt_no)' を追加して、個人別にグループ化しました。

クエリ 1。

select min(Payment.Person) 'Person',
    min(Payment.receipt_no) 'receipt_no'
from
   Payment a
where
  a.type<>'GX' and (a.reversed not in ('R') or a.reversed is null)
and a.payment_date = 
  (select min(payment_date) from Payment i 
  where i.Person=a.Person and i.type <> 'GX' 
  and (i.reversed not in ('R') or i.reversed is null))
group by a.Person

これをはるかに大きなクエリ内のサブクエリとして追加しましたが、それでも実行速度は非常に遅かったです。そのため、集計関数の使用を避けながらクエリを書き直そうとしたところ、次のようになりました。

クエリ 2。

SELECT
    receipt_no,
    person,
    payment_date,
    amount
FROM
    payment a
WHERE 
    receipt_no IN 
    (SELECT 
       top 1 i.receipt_no 
    FROM 
        payment i 
    WHERE 
        (i.reversed NOT IN ('R') OR i.reversed IS NULL) 
        AND i.type<>'GX' 
        AND i.person = a.person 
    ORDER BY i.payment_date DESC, i.receipt_no ASC)

これは必ずしもより効率的だとは思いません。実際、より大きなデータ セットに対して 2 つのクエリを並べて実行すると、クエリ 1. は数ミリ秒で完了しますが、クエリ 2. は数秒かかります。

ただし、それらをはるかに大きなクエリ内のサブクエリとして追加すると、より大きなクエリはクエリ 1 を使用して数時間で完了し、クエリ 2 を使用して 40 秒で完了します。

これは、一方では集計関数を使用し、他方では使用していないことにのみ起因すると考えられます。

4

2 に答える 2

1

支払いをどのように区別しますか

    (3|David  |15/04/2003) 
and (9|David  |15/04/2003)

これらは両方とも同じことによって行われます。時間が異なる場合を除いて、このクエリは正常に機能するはずです。

select 
    receipt_no,
    person,
    payment_date
from
    payment a
where
    type<>'GX' and (reversed not in ('R') or reversed is null)

  and payment_date = 
     (select min(payment_date) from payment i 
      where i.person=a.person and i.type <> 'GX' 
      and (i.reversed not in ('R') or i.reversed is null))
order by person,payment_date desc

SQLFiddleでこのクエリを設定してテストしましたが、データ量がないため、パフォーマンスについてはよくわかりません。だからチェックして私に知らせてください

===

上記の質問のSQLフィドルデモ

于 2012-11-07T19:01:05.950 に答える
0

CodeReview からのコメントに続いて -

また、提案されているように、 Rank()コマンドを使用してクエリを書き直しました。

クエリ 3。

left join 
    (select 
        a.Person, 
        a.amount,
        (rank () over (Partition by a.Person order by a.payment_date desc, a.receipt_no desc)) 'Ranked' 
    from 
        Payment a
    Where 
        (a.reversed not in ('R') or a.reversed is null) 
        and a.type != 'GX'
    ) [lastPayment]  
on 
    [lastPayment].Person = [Person].Person 
    and [lastPayment].ranked = 1

この方法により、より大きなクエリが高速化され、現在、より大きなクエリは約 28 秒かかります。

ただし、 Rank()は SQL 2005 以降でのみサポートされています。

于 2012-11-16T09:10:55.367 に答える