Python 如何使代码更高效?
我有一个元组列表,其中包含工具id、时间和消息。我想从这个列表中选择消息与某个字符串匹配的所有元素,以及时间在该工具的任何匹配消息的某个差异范围内的所有其他元素 以下是我目前的做法:Python 如何使代码更高效?,python,Python,我有一个元组列表,其中包含工具id、时间和消息。我想从这个列表中选择消息与某个字符串匹配的所有元素,以及时间在该工具的任何匹配消息的某个差异范围内的所有其他元素 以下是我目前的做法: # record time for each message matching the specified message for each tool messageTimes = {} for row in cdata: # tool, time, message if self.message
# 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)]
这已经足够快了,但它可能返回2万或3万行。然后我循环遍历结果集,对每一行运行一个类似这样的查询,其中dt和t是上面选择的一行中的日期、时间和工具:
SELECT date_time, message
FROM event_log
WHERE tool = t
AND ABS(TIMESTAMPDIFF(SECOND, date_time, dt)) <= timediff
那花了大约一个小时
我还尝试在一个嵌套查询中执行此操作,其中内部查询从我的第一个查询中选择行,外部查询选择时间差行。这花了更长的时间
因此,现在我选择不带“%foo%”子句这样的消息,我将返回600000行,并尝试从python中提取我想要的行 对于表格数据,您无法通过Python库,该库包含针对此类查询的高度优化的代码。优化SQL的方法是在一个查询中完成所有操作,而不是迭代20K行,然后对每个行执行另一个查询 通常这意味着您需要添加联接,或者偶尔添加子查询。是的,只要重命名一个或两个副本,就可以将表连接到自身。比如说:
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
现在,这可能还不够快,所以有两个步骤可以改进它
首先,查找任何明显需要索引的列。显然,工具和日期时间需要简单的索引。消息可能受益于一个简单的索引,或者,如果您的数据库有一些更奇特的东西,则可能受益于一些更奇特的东西,但是考虑到初始查询足够快,您可能不需要担心它
偶尔,这就足够了。但通常情况下,你无法准确地猜出所有的事情。可能还需要重新排列查询的顺序等。因此,您需要解释查询,并查看DB引擎正在执行的步骤,查看它在哪里执行缓慢的迭代查找,而它可以执行快速索引查找,或者它在一个小集合之前迭代一个大集合。我通过如下更改代码解决了这个问题: -首先,我将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秒 我又做了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)
我写这篇文章并不是为了回答这个问题,因为它不是一个答案,但根据我的经验,您应该尽可能多地使用SQL。该语言和环境针对从数据库中排序和拾取数据进行了优化。如果有什么问题的话,也许你可以在SQL中发布你是如何做到这一点的,我们可以先尝试优化它。+1到Mathias。不应该在查询之后再执行子查询,而应该在SQL中使用联接或子查询(如果不可能)执行查询。如果这花费的时间太长,几乎可以肯定的是你缺少了一个重要的索引。我更新了我的帖子,展示了我在SQL中所做的事情。没有索引可以帮助处理like。但是like查询已经进行得很快了,所以不需要帮助。您想要加快的是对20000行进行迭代,并对每个行执行30行查询,这部分不需要任何like子句。谢谢,我会检查OUT,我已经用子查询进行了尝试,但这花费了一个多小时。我将处理您的查询,看看它是如何工作的。有一个关于工具和日期时间的索引。您正在对varchar字段执行类似操作的索引没有帮助。DB是MySQL,我在这里发布的时候简化了很多查询。实际上,在event_log表中有许多使用各种外键的表的连接。el2中的selects还必须从这些表中提取外键。我不知道如何编写这样做的查询,即使用不同的选择条件从同一个表中选择两次。例如,我必须选择data_module.name两次,一次选择data_module.id=el1.module_id,另一次选择data_module.id=el2.module_id。这就是我最初使用子查询的原因。@LarryMartell:我不确定为什么您认为不能在同一查询中执行data_module.id=el1.module_id和data_module.id=el2.module_id。一个在子查询中,或者数据_模块连接两次。问题出在哪里。@LarryMartell:更重要的是,优化SQL查询的方法是优化SQL查询,而不是将其转化为一系列分层的独立查询,在这些查询中,即使在理论上,你也无法比迭代所有查询的笛卡尔积做得更好。如果它太复杂,无法阅读和推理,请将每个连接或子选择打断为单独的视图。
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)