4

Log(id、user、action、date)とActionTypes(action、type)の2つのテーブルがあります。アクションA0とタイプT0が与えられた場合、ユーザーごとに、A0の直後にアクションAiを使用した回数をカウントしたいと思いますが、タイプT0ではないLogのアクションはスキップします。したがって、たとえば:

ログ:

id   user   action        date
----------------------------------------
1    mary   start   2012-07-16 08:00:00
2    mary   open    2012-07-16 09:00:00
3    john   start   2012-07-16 09:00:00
4    mary   play    2012-07-16 10:00:00
5    john   open    2012-07-16 10:30:00
6    mary   start   2012-07-16 11:00:00
7    mary   jump    2012-07-16 12:00:00
8    mary   close   2012-07-16 13:00:00
9    mary   delete  2012-07-16 14:00:00
10   mary   start   2012-07-16 15:00:00
11   mary   open    2012-07-16 16:00:00

ActionTypes:

action  type
--------------
start   0
open    1
play    1
jump    2
close   1
delete  1

したがって、アクション「start」とタイプ「1」が与えられると、答えは次のようになります。

user   action    ntimes
------------------------
mary   open      2
mary   close     1
john   open      1

私の試みは

SELECT b.user,b.action, count(*)
FROM log a, log b
WHERE a.action='start' AND b.date>a.date AND a.user=b.user AND
      1=(select type from ActionTypes where action=b.action) AND
      not exists (SELECT c.action FROM log c where c.user=a.user AND                  
                  c.date>a.date and c.date<b.date and                            
                  1=(select type from ActionTypes where action=c.action))
GROUP BY b.user,b.action

ログテーブルには約100万のタプルがあり、クエリは機能しますが、遅すぎます。SQLServerを使用しています。それを速くする方法についてのヒントはありますか?ありがとう

4

4 に答える 4

3

このクエリを試していただけませんか?これは、以前の時系列レコードが要求されたタイプであるかどうかをテストするために存在を使用します。自己参加よりも速いと思います。デモ@SqlFiddleを配置しました

select log.[user], log.action, count(*) ntimes
  from log
 inner join actiontype t
    on log.action = t.action
 where t.type = 1
   and exists (select *
                 from 
                   (select top 1 t1.type
                      from log l1
                     inner join actiontype t1
                        on l1.action = t1.action
                     where l1.[user] = log.[user]
                       and l1.date < log.date
                       and t1.type in (0, 1)
                     order by l1.date desc
                   ) prevEntry
                where prevEntry.type = 0
               )
 group by log.[user], log.action

結果リストにmary\が含まれている理由がわかりません。close前のレコードはタイプのジャンプであり、2開始するためにスキップしないでください。

于 2012-07-18T09:38:27.603 に答える
3

@Nikola Markovinović のセットアップを借りた後、次の解決策を思いつきました。

WITH ranked AS (
  SELECT
    L1.[user],
    L2.action,
    rnk = ROW_NUMBER() OVER (PARTITION BY L1.id ORDER BY L2.date)
  FROM Log L1
    INNER JOIN Log L2 ON L2.[user] = L1.[user] AND L2.date > L1.date
    INNER JOIN ActionType at ON L2.action = at.action
  WHERE L1.action = @Action
    AND at.type   = @Type
)
SELECT
  [user],
  action,
  ntimes = COUNT(*)
FROM ranked
WHERE rnk = 1
GROUP BY
  [user],
  action
;

基本的に、このクエリLogは、指定されたアクションを持つすべてのユーザーのレコードをテーブルから選択し、そのサブセットを結合して、最初のサブセットのアクションに続く指定されたタイプのすべてのアクションを取得し、途中Logで昇順でランク付けします。dateROW_NUMBER()関数を使用)。次に、クエリはランキングが の行のみを取得し、 および1でグループ化し、グループ内の行をカウントします。useraction

SQL Fiddle で実際の例を確認 (および操作) できます。

于 2012-07-18T10:53:28.597 に答える
2

アクションクエリとすべての関係フィールドは、文字列ではなく整数である方がはるかに高速です。

クエリを高速化する唯一の方法は、データベースの構造を変更することです。リレーションにはインデックスを付ける必要があり、文字列ではなく整数にする必要があります。たとえば、次のようなものです。

id   user   action        date
----------------------------------------
1    mary   1   2012-07-16 08:00:00
2    mary   2   2012-07-16 09:00:00
3    john   3   2012-07-16 09:00:00
4    mary   1   2012-07-16 10:00:00
5    john   3   2012-07-16 10:30:00
6    mary   4   2012-07-16 11:00:00
7    mary   5   2012-07-16 12:00:00
8    mary   6   2012-07-16 13:00:00
9    mary   1   2012-07-16 14:00:00
10   mary   3   2012-07-16 15:00:00
11   mary   1   2012-07-16 16:00:00

あなたの問題を解決します。

さらに、1 ~ 9 個のアクション タイプがある場合は、tinyint に対するアクションを実行できます。また、id と tinyint をプライマリ キーに追加すると、(単純な結合を使用して) クエリが確実に簡単になり、データベースもより多くなります。将来の変更に柔軟に対応できます。たとえば、次のことができます。

id action  type
--------------
1  start   0
2  open    1
3  play    1
4  jump    2
5  close   1
6  delete  1

ここで、id は主キーであり、「ログ」テーブルの「アクション」には、この ID への外部キーがあります。

主な問題は、インデックスと外部キーの関係がないことだと思います。

于 2012-07-18T07:12:56.527 に答える
0

私は次の声明に少し同意しません。

  1. ...文字列ではなく整数である方がはるかに高速です

    actionにインデックスが付けられると、整数と文字列の違いはほとんどありません。

  2. ...クエリを高速化する唯一の方法は、データベースの構造を変更することです

    この場合、クエリはさまざまな方法で最適化できます。

    • 結合されたデータ セット (Log x ActionTypes) でのフィルタリングを避け、以前にフィルタリングを試みます (以下の例では、フィルタリングは内部サブ選択で行われます)。
    • フィルター条件 (where) の繰り返しは避けてください。SQLサーバーは内部的にこのクエリアの重複を最適化しますが、通常、計算を数回行っていることを示しており、ほとんどの場合、条件を1回だけ配置できるソリューションを見つけることができます(以下の例では、where条件を前に配置できますgroup by)。
    • あなたの親友は「SQL Query Analyzer(Optimizer)」です。Sql Server Manager Studio の組み込みツールであり、データ量を考慮して SQL クエリの実行コストを表示します。これは非常に優れたツールであり、クエリのボトルネックを見つけるのに役立ちます。

    これは、必要な結果を生成する単純化されたクエリです(ms sql server で作業してからしばらく経っているため、Oracleで記述およびテストされています):

select
  "user",
  action,
  count(*)
from action_log
where action not in ( --exclusion criteria
    select action_type."action"from action_type where action_type."type" = 1
)
group by "user", action
于 2012-07-18T08:06:50.100 に答える