我有一个永远运行的MySQL(v5.7.26)查询。这是查询:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY
)
AND r.type = 'WORKOUT'
GROUP BY ur.user_id;
通过尝试了解问题出在哪里,我对此做了一些尝试。出于测试目的,我尝试分成两部分。所以:
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY;
(非常快速地)返回用户user_id的列表。 当我将返回的结果插入查询的第一部分时,如下所示:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (1,1,1,4,4,5,6,7,7,7);
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
它运行非常快。我的假设是IN(子查询)是瓶颈。
我当时正在考虑提取子查询并获取user_ids,然后将其用作变量,但是我不确定这是否是一种好方法,此外,我还遇到了问题。这是我的尝试:
-- first statement
SET @v1 = (SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
-- second statement
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND ur.user_id IN (@v1);
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
这里的问题是第一条语句返回错误:
子查询返回1行以上。
预期结果是user_id,可以重复。我需要那些重复的计数。
我该如何解决?
答案 0 :(得分:0)
尝试用EXISTS
代替IN
...
AND EXISTS (SELECT *
FROM user_resource ur2
WHERE ur2.user_id = ur.user_id
AND ur2.action_date >= now() - INTERVAL 2 DAY)
...
以及user_resource (user_id, action_date)
,user_resource (status, action_date, user_id)
和/或user_resource (type)
上的索引。
答案 1 :(得分:0)
您可以尝试:
-- first statement
SET @v1 = (SELECT GROUP_CONCAT(user_id)
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY)
-- second statement
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS prefixes
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
WHERE ur.status = 1 AND NOT ur.action_date IS NULL AND FIND_IN_SET(ur.user_id,@v1)
AND r.type = 'WORKOUT'
GROUP BY ur.user_id
答案 2 :(得分:0)
其他联接将比子查询更快:
SELECT
ur.user_id AS user_id,
sum(r.duration) AS total_time,
count(user_id) AS number_of_workouts
FROM user_resource ur
INNER JOIN resource r ON r.id = ur.resource_id
INNER JOIN (
SELECT user_id
FROM user_resource ur2
WHERE ur2.action_date >= now() - INTERVAL 2 DAY
) t ON t.user_id = ur.user_id
WHERE
ur.status = 1
AND NOT ur.action_date IS NULL
AND r.type = 'WORKOUT'
GROUP BY ur.user_id;