【发布时间】:2022-01-10 23:07:58
【问题描述】:
我有 2 个共享一对多关系的表。假设如下结构:
users users_metadata
------------- -------------
id | email id | user_id | type | score
一个用户可以有很多元数据。 users 表有 100k 行,users_metadata 表有 300k 行。它可能会增长 10 倍,因此无论我写什么都需要对大量数据进行优化。
我需要编写一个 sql 语句,它只返回通过元数据表中的几个不同分数条件的用户电子邮件。
// if type = 1 and if score > 75 then <1 point> else <0 points>
// if type = 2 and if score > 100 then <1 point> else <0 points>
// if type = 3 and if score > 0 then [-10 points] else <0 points>
// there are other types that we want to ignore in the score calculations
如果用户通过阈值(例如 >= 1 分),那么我希望该用户出现在结果集中,否则我希望忽略该用户。
我已经尝试使用一个存储函数/游标来获取 user_id 并遍历元数据以找出点,但结果执行非常慢(尽管它确实有效)。
目前我有这个,执行大约需要 1 到 3 秒。
SELECT u.id, u.email,
(
SELECT
SUM(
IF(k.type = 1, IF(k.score > 75, 1, 0), 0) +
IF(k.type = 2, IF(k.score > 100, 1, 0), 0) +
IF(k.type = 3, IF(k.score > 0, 1, -10), 0)
)
FROM user_metadata k WHERE k.user_id = u.id
) AS total
FROM users u GROUP BY u.id HAVING total IS NOT NULL;
我觉得在 10 倍时这会更慢。 1 到 3 秒的查询执行时间对于我已经需要的东西来说太慢了。
更理想的方法是什么?
如果我也为此使用 PHP 之类的语言,将运行 2 个查询,一个从 user_metadata 的仅 passing 用户中获取 user_ids,然后第二个在该 ID 列表上选择 WHERE IN更好?
【问题讨论】:
-
这是一个报告类型的查询,需要扫描数百万行。你不能指望这个查询运行得非常快。
-
这很公平。如果 1 到 3 秒的运行速度可以接受,那么我可以接受。我将在下面使用@Barmar 的解决方案,以便更轻松地运行条件检查。我可以将结果限制为 25 并且仅显示每页,因此对于加载此信息的管理页面,查询应该是可以接受的(
-
实际上,有限选择的连接方法比我上面的嵌套选择查询慢得多。 JOIN 对 25 个结果运行查询需要 500 毫秒。嵌套选择需要 32 毫秒
-
请为每张桌子提供
SHOW CREATE TABLE,以及每张桌子的大致大小。由于ORDER BY和LIMIT可以在优化方面产生很大 的差异,请提供完整的查询!你的时间证明了这一点! -
type的分布是什么?值是否只有 1、2、3?如果不是,有多少百分比不是 1 或 2 或 3?
标签: php mysql sql query-optimization