2

このクエリ01:30の実行には約時間がかかります。

select DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn]))
       , count(t2.UserId)
       , count(*) - count(t2.UserId)
from Events t1
left join (select c.UserId, min(c.OccurredOn) FirstOccurred
           from Events c
           where [OccurredOn] between @start and @end
           group by c.UserId) t2 on t1.OccurredOn = t2.FirstOccurred and t1.UserId = t2.UserId
where t1.EventType = @eventType
    and t1.[OccurredOn] between @start and @end
group by DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn]))
order by DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn]))

サブクエリから句を削除すると、WHEREすぐに実行されます。

WHEREテイク < 1でサブクエリを単独で実行する

最初にテーブル変数にSELECTサブクエリを実行し、それに結合すると、クエリ全体が 19 秒で実行されます。

Events表は次のようになります。

[Events](
    [EventType] [uniqueidentifier] NOT NULL,
    [UserId] [uniqueidentifier] NOT NULL,
    [OccurredOn] [datetime] NOT NULL,
)

次のprimary, nonclustered, nouniqueインデックスがあります。

  • イベントタイプ
  • ユーザーID
  • 発生日

実行計画はこちら

ここに画像の説明を入力

SQL Server 2008 を使用しています。

2つのこと:

  1. これを遅くするために何が起こっていますか?
  2. どうすれば高速化できますか?
4

2 に答える 2

1

順序付けはオンザフライ計算(DATEADD(dd, 0, DATEDIFF(dd, 0, t1.[OccurredOn])))に依存しているため、クエリは低速です。SQLServerはオンザフライ計算でインデックスを利用できません。

Postgresqlには式のインデックスがあります。Postgresqlを使用すると、基本的に式の結果を実際の列(舞台裏の列)に永続化できるため、その式で並べ替える必要がある場合は、Postgresqlでインデックスを使用できます。その表現に。

SQL Serverに最も近い類似機能は、永続化された数式です。

このサンプルクエリを使用すると、機能を簡単に確認できます。

create table PersonX
(
Lastname varchar(50) not null,
Firstname varchar(50) not null
);

create table PersonY
(
Lastname varchar(50) not null,
Firstname varchar(50) not null
);


alter table PersonX add Fullname as Lastname + ', ' + Firstname PERSISTED;    
create index ix_PersonX on PersonX(Fullname);

declare @i int = 0;

while @i < 10000 begin
    insert into PersonX(Lastname,Firstname) values('Lennon','John');
    insert into PersonY(Lastname,Firstname) values('Lennon','John');
    set @i = @i + 1;
end;


select top 1000 Lastname, Firstname
from PersonX
order by Fullname;


select top 1000 Lastname, Firstname
from PersonY
order by Lastname + ', ' + Firstname;

PersonXでフルネームで注文を行うと、PersonYよりも高速になります。PersonXのクエリコストは32%のみですが、PersonYは68%です。

クエリのパフォーマンスを解決するには、次の手順を実行します。

alter table Events 
    add OccurenceGroup as 
        DATEADD(dd, 0, DATEDIFF(dd, 0, [OccurredOn])) PERSISTED

create index ix_Events on Events(OccurenceGroup);

次に、OccurenceGroupでグループ化と順序付けを行います。


ちなみに、OccuredOnとEventTypeにインデックスを追加しましたか?

于 2012-07-21T09:44:28.483 に答える
1

ユーザーごとに何度も再計算するのではなく、派生テーブルが 1 回だけ計算されるようにLEFT JOIN置き換えることができます。LEFT MERGE JOINt2MIN

以下のようにランキング関数を使用してこれを書き直すこともできます。安いかもしれません。これらのアイデアをデータとインデックスに対してテストする必要があります。

;WITH T AS
(
SELECT *,
       RANK() OVER (PARTITION BY UserId ORDER BY OccurredOn) AS Rnk
FROM Events
WHERE [OccurredOn] BETWEEN @start AND @end
)
SELECT Dateadd(dd, 0, Datediff(dd, 0, OccurredOn)),
       COUNT(CASE WHEN Rnk =1 THEN 1 END),
       COUNT(CASE WHEN Rnk >1 THEN 1 END)
FROM T
WHERE EventType = @eventType      
GROUP BY Dateadd(dd, 0, Datediff(dd, 0, OccurredOn)) 
ORDER BY Dateadd(dd, 0, Datediff(dd, 0, OccurredOn)) 
于 2012-07-21T11:13:24.460 に答える