【问题标题】:Sqlalchemy mySQL optimize querySqlalchemy mySQL 优化查询
【发布时间】:2019-01-16 16:03:31
【问题描述】:

一般:

我需要从具有数十万个条目的给定数据库创建一个统计工具。所以我永远不需要写入数据库,只需要获取数据。

问题:

我有一个用户表,在我的情况下,我选择 20k 用户(在两个日期之间)。现在我只需要选择至少花钱过一次的用户(来自这些20k)。

为此,我有 3 个不同的表来保存数据,无论用户是否花钱。 (所以我们在这里总共使用 4 个表):

User, Transaction_1, Transaction_2, Transaction_3

到目前为止我做了什么:

在 User 类的模型中,我创建了一个属性,用于检查用户是否在其中一个事务表中出现过一次:

@property
def spent_money_once(self):
    spent_money_atleast_once = False
    in_transactions = Transaction_1.query.filter(Transaction_1.user_id == self.id).first()
    if in_transactions:
        spent_money_atleast_once = True
        return spent_money_atleast_once
    in_transactionsVK = Transaction_2.query.filter(Transaction_2.user_id == self.id).first()
    if in_transactionsVK:
        spent_money_atleast_once = True
        return spent_money_atleast_once
    in_transactionsStripe = Transaction_3.query.filter(Transaction_3.user_id == self.id).first()
    if in_transactionsStripe:
        spent_money_atleast_once = True
        return spent_money_atleast_once
    return spent_money_atleast_once

然后我为男性和女性用户创建了两个计数器,因此我可以计算这 2 万用户中有多少人至少花钱过一次:

males_payed_atleast_once = 0
females_payed_atleast_once = 0

for male_user in male_users.all():
    if male_user.spent_money_once is True:
        males_payed_atleast_once += 1

for female_user in female_users.all():
    if female_user.spent_money_once is True:
        females_payed_atleast_once += 1

但这需要很长时间来计算,大约 40-60 分钟。我从来没有处理过这么大的数据量,也许这很正常?

其他信息:

如果您想知道 male_usersfemale_users 的样子:

# Note: is this even efficient, if all() completes the query than I need to store the .all() into variables, otherwise everytime I call .all() it takes time
global all_users
global male_users
global female_users

all_users = Users.query.filter(Users.date_added >= start_date, Users.date_added <= end_date)
male_users = Users.query.filter(Users.date_added >= start_date, Users.date_added <= end_date, Users.gender == "1")
female_users = Users.query.filter(Users.date_added >= start_date, Users.date_added <= end_date, Users.gender == "2")

我正在尝试将某些查询保存在全局变量中以提高性能。

我正在使用 Python 3 |烧瓶 |用于此任务的 Sqlalchemy。数据库是 MySQL。

【问题讨论】:

  • 3张事务表的意义是什么?您需要计算 each 表中的重复次数吗?还是仅结合所有 3 个?

标签: python mysql sql performance sqlalchemy


【解决方案1】:

我现在尝试了一种完全不同的方法并使用了join,现在它更快了,它在 10 秒内完成了查询,耗时 60 分钟。~:

# males
paying_males_1 = male_users.join(Transaction_1, Transaction_1.user_id == Users.id).all()
paying_males_2 = male_users.join(Transaction_2, Transaction_2.user_id == Users.id).all()
paying_males_3 = male_users.join(Transaction_3, Transaction_3.user_id == Users.id).all()

males_payed_all = paying_males_1 + paying_males_2 + paying_males_3
males_payed_atleast_once = len(set(males_payed_all))

我只是加入每个表并使用.all(),结果很简单lists。之后,我将合并所有列表并将它们类型转换为set。现在我只有唯一用户。最后一步是在set 上使用len() 对它们进行计数。

【讨论】:

  • 您可以使用count()获取记录数量。例如:male_payed_count = Users.query.filter(Users.date_added &gt;= start_date, Users.date_added &lt;= end_date, Users.gender == "1", Users.spent_money_once == True).count()。没有lenset
  • 这很遗憾行不通,我也试过了,但什么都不算。男性和女性返回 0
  • 这可能可以通过 hybrid_property 来完成,但是在更改为混合后我得到了奇怪的结果,它只是计算了所有记录而忽略了花费的钱 == True。我暂时保持原样
【解决方案2】:

假设您需要在计数之前将3个表的信息汇总在一起,这样会快一点:

SELECT userid, SUM(ct) AS total
  FROM (
    ( SELECT userid, COUNT(*) AS ct FROM trans1 GROUP BY userid )
    UNION ALL
    ( SELECT userid, COUNT(*) AS ct FROM trans2 GROUP BY userid )
    UNION ALL
    ( SELECT userid, COUNT(*) AS ct FROM trans3 GROUP BY userid )
       )
  GROUP BY userid
  HAVING total >= 2

建议你在 mysql 命令行工具中测试一下,然后弄清楚如何将其转换为 Python 3 |烧瓶 |炼金术

关于“隐藏数据库”的软件包的有趣之处 --;如果您要做任何重要的事情,您仍然需要了解数据库的工作原理。

【讨论】:

    猜你喜欢
    • 2011-01-22
    • 2011-07-07
    • 2018-12-21
    • 2010-12-15
    • 2011-11-04
    • 2016-01-25
    相关资源
    最近更新 更多