用Python实现PostgreSQL数据库的快速复杂SQL查询
我有一个庞大的数据集,在PostgreSQL数据库中有+50万条记录,需要大量的计算和内部连接 Python是Psycopg2的首选工具 使用20000条记录的fetchmany运行进程需要几个小时才能完成 执行需要按顺序进行,因为需要分别获取50M的每个记录,然后需要运行另一个查询(在下面的示例中),然后返回结果并将其保存在单独的表中 在每个表上正确配置索引(总共5个表),复杂查询(返回计算值-下面的示例)大约需要240MS才能返回结果(当数据库未加载时) 芹菜用于在单独的表中插入计算值的数据库 我的问题是关于减少总体运行时间和更快生成结果/计算的常见策略。 换句话说,一个接一个地检查所有记录,通过第二次查询计算字段值,然后保存结果的有效方法是什么 更新: 在试图混淆敏感细节时,我无意中漏掉了一条重要信息。对不起 原始的用Python实现PostgreSQL数据库的快速复杂SQL查询,python,database,postgresql,performance,pandas,Python,Database,Postgresql,Performance,Pandas,我有一个庞大的数据集,在PostgreSQL数据库中有+50万条记录,需要大量的计算和内部连接 Python是Psycopg2的首选工具 使用20000条记录的fetchmany运行进程需要几个小时才能完成 执行需要按顺序进行,因为需要分别获取50M的每个记录,然后需要运行另一个查询(在下面的示例中),然后返回结果并将其保存在单独的表中 在每个表上正确配置索引(总共5个表),复杂查询(返回计算值-下面的示例)大约需要240MS才能返回结果(当数据库未加载时) 芹菜用于在单独的表中插入计算值的数据
SELECT
查询计算从不同表聚合的值,如下所示:
SELECT CR.gg, (AX.b + BF.f)/CR.d AS calculated_field
FROM table_one CR
LEFT JOIN table_two AX ON EX.x = CR.x
LEFT JOIN table_three BF ON BF.x = CR.x
WHERE CR.gg = '123'
GROUP BY CR.gg;
PS:SQL查询是由我们经验丰富的DBA编写的,因此我相信它已得到优化。因为您正在查询中执行联接,所以逻辑上要做的是解决它,这意味着创建一个汇总表,这个汇总表(位于数据库中)将保存最终联接的数据集,因此,在python代码中,您只需从中获取/选择数据
另一种方法是使用物化视图因为您在查询中进行连接,所以逻辑上要做的事情是绕过它,这意味着创建一个汇总表,这个汇总表(位于数据库中)将保存最终的连接数据集,因此在python代码中,您只需从中获取/选择数据 另一种方法是使用物化视图
- 不要循环记录,并为每个记录重复调用DBMS
- 相反,让DBMS处理大块(最好是全部)数据
- 然后,让它说出所有的结果
我接受了@wildplasser的建议,将计算操作作为一个函数移动到数据库中。 结果非常有效,至少可以说总运行时间降到了分钟/小时 重述:
- 不再按顺序提取数据库记录 前面提到的
- 计算通过函数在数据库内部进行
- 不再按顺序提取数据库记录 前面提到的
- 计算通过函数在数据库内部进行
。。。单个查询大约需要…
什么是单个查询???@wildplasser每次查询数据集时的单个查询(包括连接命令),即:select*from_one er left join table_two ex ON ex.x=er.y left join table_three ef ON ef.x=er.z其中er.gg='123'group by er.gg代码>和:什么是非单一查询???注意:er.gg的分组是没有用的,因为只有一个组(没有聚合):不要这样做!(每项需要两次往返数据库+解析+执行)相反,让数据库计算您需要的所有记录的所有值,并在一次扫描中获取所有结果。在psql中运行相同的查询,以获得与pyhon前端之间传输所需的估计时间,以及在python结构中转换数据的时间。。。。单个查询大约需要…
什么是单个查询???@wildplasser每次查询数据集时的单个查询(包括连接命令),即:select*from_one er left join table_two ex ON ex.x=er.y left join table_three ef ON ef.x=er.z其中er.gg='123'group by er.gg代码>和:什么是非单一查询???注意:er.gg的分组是没有用的,因为只有一个组(没有聚合):不要这样做!(每个项目需要两次往返数据库+解析+执行)相反,让数据库计算您需要的所有记录的所有值,并在一次扫描中获取所有结果。感谢您详细的回答。你的观点完全有道理,而不是关于查询的观点,因为我犯了一个错误,并添加了一个更新来澄清问题。另外,感谢您的代码片段。注意:范围中没有ez.
别名。请发布真正的查询(加上表定义,等等,plonk?)谢谢你详细的回答。你的观点完全有道理,而不是关于查询的观点,因为我犯了一个错误,并添加了一个更新来澄清问题。另外,感谢您的代码片段。注意:范围中没有ez.
别名。请发布真实的查询(加上表格)
def fetch_referred_tweets(self):
self.curs = self.conn.cursor()
tups = ()
selrefd = """SELECT twx.id, twx.in_reply_to_id, twx.seq, twx.created_at
FROM(
SELECT tw1.id, tw1.in_reply_to_id, tw1.seq, tw1.created_at
FROM tt_tweets tw1
WHERE 1=1
AND tw1.in_reply_to_id > 0
AND tw1.is_retweet = False
AND tw1.did_resolve = False
AND NOT EXISTS ( SELECT * FROM tweets nx
WHERE nx.id = tw1.in_reply_to_id)
AND NOT EXISTS ( SELECT * FROM tt_tweets nx
WHERE nx.id = tw1.in_reply_to_id)
UNION ALL
SELECT tw2.id, tw2.in_reply_to_id, tw2.seq, tw2.created_at
FROM tweets tw2
WHERE 1=1
AND tw2.in_reply_to_id > 0
AND tw2.is_retweet = False
AND tw2.did_resolve = False
AND NOT EXISTS ( SELECT * FROM tweets nx
WHERE nx.id = tw2.in_reply_to_id)
AND NOT EXISTS ( SELECT * FROM tt_tweets nx
WHERE nx.id = tw2.in_reply_to_id)
-- ORDER BY tw2.created_at DESC
)twx
LIMIT %s;"""
# -- AND tw.created_at < now() - '15 min':: interval
# -- AND tw.created_at >= now() - '72 hour':: interval
count = 0
uniqs = 0
self.curs.execute(selrefd, (quotum_referred_tweets, ) )
tups = self.curs.fetchmany(quotum_referred_tweets)
for tup in tups:
if tup == None: break
print ('%d -->> %d [seq=%d] datum=%s' % tup)
self.resolve_list.append(tup[0] ) # this tweet
if tup[1] not in self.refetch_tweets:
self.refetch_tweets[ tup[1] ] = [ tup[0]] # referred tweet
uniqs += 1
count += 1
self.curs.close()
select er.gg, er.z, er.y
from table_one er
where er.gg = '123'
-- or:
where er.gg >= '123'
and er.gg <= '456'
ORDER BY er.gg, er.z, er.y -- Or: some other ordering
;