2011-01-20 3 views
0

大きなイベント発生テーブルがあります。私はいくつかのイベントシーケンスを実行し、すべてのユーザーを知っていただきたいと思い SQL - シーケンス/パスの検索

  • ユーザーID
  • のEventId(このイベントが発生した)(イベントの種類)
  • タイムスタンプ:これは、次の列があります日付範囲の間3.

    は現在、私はちょうどCLRストアドプロシージャを使用してレコードセットを反復処理しています前に発生した、イベント1が2と2の前に起こるべきである

    私はイベントを探していた場合は1-2-3の配列を決定... 。このアプローチは遅いです。 SQLでこれを行うより良い方法はありますか?

    私はSQl Server 2008を使用しています。また、userIdごとに重複するeventIdが存在する可能性があります。

    表のサイズは約3〜40億行で、日付範囲には約10億行が含まれます。パフォーマンスは重要です。

    ありがとうございました

+0

CLRは2005/2008を意味します - それは?答えに影響を与える可能性があります。 –

+0

UserIdごとに重複するeventIDが存在する可能性がありますか?もしそうなら、あなたのロジックにどのように影響しますか? –

+0

希望の期間に存在する可能性のある行の量はいくらですか?インデックスは何ですか? – DVK

答えて

-2

これはなんですか?

 
select userid, eventid, theTime 
from eventTable 
where theTime between '01/01/2000' and '01/01/2001' 
order by theTime DESC 

+0

どのようにして特定のイベントが連続しているユーザーになるのですか? – StackUnderflow

+0

useridでソートされた項目を最初に取得するには、date ... "order by theTime DESC"から "userid ASC、theTime DESC"で順番に行を変更してください。 –

+0

これは壊れています。 – DVK

0

あなたが探している事前に知ってシーケンスすることができ、そしてそれはあまりにも長くはない場合、あなたが欲しいテーブルのサブセットを選択することができます(日付範囲に対処し、1つのイベントIDを選び出すため)必要に応じてそれ自身のコピーを数多く結合し、次に日付(event1)>日付(event2)AND日付(event2)>日付(event3)の行をSELECTします。私はそれを入力していない理由はかなり長いクエリだろうが、あまりにも非効率でなくても動作する必要があります。

EDIT:例:

SELECT a.userID,a.date,b.date,c.date FROM 
    (SELECT * FROM `events` WHERE `date` BETWEEN $date1 AND $date2 AND `type`=$type1) a 
    LEFT JOIN (SELECT * FROM `events` WHERE `date` BETWEEN $date1 AND $date2 AND `type`=$type2) b ON a.userID=b.userID 
    LEFT JOIN (SELECT * FROM `events` WHERE `date` BETWEEN $date1 AND $date2 AND `type`=$type3) c ON a.userID=c.userID 
    WHERE a.date > b.date AND b.date > c.date 
+0

テーブルのサイズは約3-40億行..ユーザーは10億行で構成される日付範囲を選択することができます..これは、メモリの多くを必要とする非効率的な可能性があります。 – StackUnderflow

+0

ああ、イベントの種類も選択しています。あなたもuserIDに参加しているので、それほど悪くないかもしれません。私はそれを意味するものを編集します。 – zebediah49

+0

OK、私は何を意味するのかの例を加えました。私はいくつかの愚かな間違いをしているかもしれませんが、サブクエリは1000のイベントタイプで1M行しか選択していないことを意味し、別の1M行(OK、これはまだ多い)ユーザーIDによるフィルタリング(おそらくこれをもう少し減らす必要があります)、再度参加してください。あなたはそれを試して、それがどのように行ったか見る必要があります。もしあなたがそうしなければならなかったなら、おそらく2番目のJOINの前にa> bの日付フィルタリングを行うことを、それを入れ子にすることができます。 – zebediah49

0

あなたは(あなたがそれをコーディングするか、しているときに、発信者コードが動的に生成するときのいずれか)、することができますクエリを書いている時点で正確な配列を知っていると仮定すると、シーケンスが長すぎない限り、次の操作を行います。

select * 
from eventTable1 T1, eventTable1 T2, eventTable1 T3, 
where t1.theTime between '01/01/2000' and '01/01/2001' 
    and t2.theTime between '01/01/2000' and '01/01/2001' 
    and t3.theTime between '01/01/2000' and '01/01/2001' 
    and t1.theTime <= t2.theTime 
    and t2.theTime <= t3.theTime 
    and t1.eventId = 1 
    and t2.eventId = 2 
    and t3.eventId = 3 
    and t1.userId = t2.userId 
    and t1.userId = t3.userId 
    and t2.userId = t3.userId -- Needed for performance reasons 

あなたはuserId, theTimeのインデックスを持っていると行の量は、(特定の期間のために管理可能である場合、これはかなりうまく動作します例えば、あなたが得ることはありませんその

データセットと時間枠のスパンに応じて、上記のことが可能であることに注意してください(そしておそらくSHOULD)。その後、一時テーブルに対して上記の結合を実行します。この最適化は、指定した期間内の行の量を管理できる場合に最も効果的です(たとえば、< 100k?)と別のアプローチは、JOINを回避し、単にユーザごとに結合されたすべての配列を取得することができるtheTime


にインデックスがあります。その後、発信者のコードで「この正しい順序である」ん:

SELECT * FROM eventTable 
ORDER BY userId, theTime -- works MUCH better if this is an covering index 

そして、呼び出し元のコードでは、あなたは基本的には、ユーザごとのシーケンスを上一致するサブセットは(私には些細なようだんが、お気軽に

これはユーザごとの処理がかなり多いため、ユーザの塊を選ぶことでメモリを吹き飛ばすのを防ぐことができます(ユーザあたりのおおよそのイベント数を取得してからグラブする多くのユーザーがあなたのメモリにとって安全です - SQLが "TOP"または "LIMIT"構文をサポートしている必要があり、tempテーブル内のすべてのユーザーのリストをあらかじめ作成しておく必要があります。

+0

データ量は非常に大きいです...ネットワーク上のデータ転送はギグで実行されます..これを避けるには、現在CLRのprocを使用していますが、ロジックがシーケンシャルなので遅いです。私はマルチスレッドを行うことはできませんCLRストアドプロシージャ..同じボックスでマルチスレッドを実行するために別のサーバを実行することはオプションではありません。 – StackUnderflow

+0

@StackUnderflow - それで、インデックス付きのユーザリスト(およびメインテーブルのユーザ+インデックスのインデックス)その後、ユーザーによるチャンクを行います。こうすることで、データの処理を行うアプリケーションサーバーを並列化し、完了したら次のユーザーのデータを要求することができます。 – DVK