3

tool_id、時間、およびメッセージを含むタプルのリストがあります。このリストから、メッセージがいくつかの文字列と一致するすべての要素と、時間がそのツールの一致するメッセージの差分内にある他のすべての要素を選択したいと思います。

これが私が現在これを行っている方法です:

# record time for each message matching the specified message for each tool 
messageTimes = {} 
for row in cdata:   # tool, time, message 
    if self.message in row[2]: 
        messageTimes[row[0], row[1]] = 1 

# now pull out each message that is within the time diff for each matched message 
# as well as the matched messages themselves 

def determine(tup): 
    if self.message in tup[2]: return True      # matched message 

    for (tool, date_time) in messageTimes: 
        if tool == tup[0]: 
            if abs(date_time-tup[1]) <= tdiff: 
               return True 

    return False 


cdata[:] = [tup for tup in cdata if determine(tup)] 

このコードは機能しますが、実行に時間がかかりすぎます。たとえば、cdataに600,000個の要素がある場合(これは私のアプリでは一般的です)、実行には2時間かかります。

このデータはデータベースからのものです。元々、SQLを使用して必要なデータだけを取得していましたが、それも時間がかかりすぎました。必要なメッセージだけを選択し、次に別のクエリを実行するメッセージごとに、それぞれの時間差内でデータを取得していました。その結果、何万ものクエリが発生しました。そこで、すべての潜在的な一致を一度にプルしてから、Pythonで処理するように変更しました。これは、より高速になると考えています。多分私は間違っていた。

誰かがこれをスピードアップするためのいくつかの提案を私に与えることができますか?

提案されたようにSQLで何をしたかを示すために投稿を更新します。

SQLで行ったことは非常に簡単でした。最初のクエリは次のようなものでした。

SELECT tool, date_time, message 
FROM event_log
WHERE message LIKE '%foo%'
AND other selection criteria

これは十分に高速でしたが、2万または3万行を返す可能性があります。次に、結果セットをループし、各行に対して次のようなクエリを実行しました(dtとtは、上記の行からのdate_timeとツールです)。

SELECT date_time, message
FROM event_log
WHERE tool = t 
AND ABS(TIMESTAMPDIFF(SECOND, date_time, dt)) <= timediff

それは約1時間かかりました。

また、内側のクエリが最初のクエリから行を選択し、外側のクエリが時間差分行を選択する1つのネストされたクエリで実行しようとしました。それにはさらに時間がかかりました。

だから今私はメッセージLIKE'%foo%'句なしで選択していて、600,000行を取り戻し、Pythonから必要な行を引き出そうとしています。

4

4 に答える 4

6

SQLを最適化する方法は、2万行を超えて繰り返し、各行に対して別のクエリを実行するのではなく、すべてを1つのクエリで実行することです。

通常、これは、JOIN、または場合によってはサブクエリを追加する必要があることを意味します。はい、一方または両方のコピーの名前を変更する限り、テーブルをそれ自体に結合できます。だから、このようなもの:

SELECT el2.date_time, el2.message 
FROM event_log as el1 JOIN event_log as el2
WHERE el1.message LIKE '%foo%'
AND other selection criteria
AND el2.tool = el1.tool
AND ABS(TIMESTAMPDIFF(SECOND, el2.datetime, el1.datetime)) <= el1.timediff

さて、これはおそらく箱から出して十分な速さではないので、それを改善するための2つのステップがあります。

まず、明らかにインデックスを作成する必要がある列を探します。明らかtoolに、datetime単純なインデックスが必要です。message単純なインデックス、またはデータベースにもっと凝ったものがある場合は、もっと凝ったものがあるかもしれませんが、最初のクエリが十分に高速であったことを考えると、おそらくそれについて心配する必要はありません。

時折、それで十分です。しかし、通常、すべてを正しく推測することはできません。また、クエリの順序などを並べ替える必要がある場合もあります。そのためEXPLAIN、クエリを実行し、DBエンジンが実行している手順を調べて、低速の反復ルックアップが実行されている場所を確認します。高速なインデックスルックアップを実行している場合や、小さなコレクションの前に大きなコレクションを反復処理している場合があります。

于 2012-12-21T01:30:01.363 に答える
2

表形式のデータの場合、このようなクエリ用に高度に最適化されたコードが含まれているPythonpandasライブラリを通過することはできません。

于 2012-12-21T01:03:04.533 に答える
0

次のようにコードを変更して、これを修正しました。

-最初に、messageTimesをツールによってキー設定されたリストの辞書にしました。

messageTimes = defaultdict(list)    # a dict with sorted lists

for row in cdata:   # tool, time, module, message
    if self.message in row[3]:
        messageTimes[row[0]].append(row[1])

-次に、determine関数でbisectを使用しました。

 def determine(tup):
    if self.message in tup[3]: return True      # matched message

    times = messageTimes[tup[0]]
    le = bisect.bisect_right(times, tup[1])
    ge = bisect.bisect_left(times, tup[1])
    return (le and tup[1]-times[le-1] <= tdiff) or (ge != len(times) and times[ge]-tup[1] <= tdiff)

これらの変更により、2時間以上かかっていたコードは20分未満かかり、さらに良いことに、40分かかっていたクエリは8秒かかりました。

于 2012-12-21T20:36:50.030 に答える
0

さらに2つの変更を加えたところ、20分のクエリに3分かかりました。

found = defaultdict(int)
def determine(tup):
    if self.message in tup[3]: return True      # matched message

    times = messageTimes[tup[0]]
    idx = found[tup[0]]
    le = bisect.bisect_right(times, tup[1], idx)
    idx = le
    return (le and tup[1]-times[le-1] <= tdiff) or (le != len(times) and times[le]-tup[1] <= tdiff)
于 2012-12-24T18:05:35.760 に答える