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

这已经足够快了,但它可能会返回 20 或 30,000 行。然后我遍历结果集,并为每一行运行一个这样的查询(其中 dt 和 t 是上面选择的一行中的 date_time 和工具):

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

那花了大约一个小时。

我还尝试在一个嵌套查询中执行,其中内部查询从我的第一个查询中选择行,外部查询选择时间差异行。那花了更长的时间。

所以现在我选择没有消息 LIKE '%foo%' 子句,我正在取回 600,000 行并试图从 python 中提取我想要的行。

4

4 回答 4

6

优化 SQL 的方法是在一个查询中完成所有操作,而不是遍历 20K 行并为每一行执行另一个查询。

通常这意味着您需要添加一个 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

现在,开箱即用可能不够快,因此有两个步骤可以改进它。

首先,查找任何明显需要索引的列。显然tool并且datetime需要简单的索引。message可能会受益于简单的索引,或者,如果您的数据库有更高级的东西,也许更高级的东西,但鉴于初始查询足够快,您可能不需要担心它。

偶尔,这就足够了。但通常,您无法正确猜测所有内容。并且可能还需要重新排列查询的顺序等。因此,您将需要EXPLAIN查询,并查看数据库引擎正在执行的步骤,并查看它在哪里进行缓慢的迭代查找可能正在执行快速索引查找,或者它在一个小集合之前迭代一个大集合。

于 2012-12-21T01:30:01.363 回答
2

对于表格数据,您不能跳过 Python pandas库,其中包含针对此类查询的高度优化代码。

于 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])

- 然后在确定函数中我使用了二分法:

 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 回答