我如何优化mysql查询需要超过4分钟

g2ieeal7  于 2023-03-22  发布在  Mysql
关注(0)|答案(4)|浏览(108)

如何优化以下查询:-

SELECT
  YEAR(msa.DATE) AS Year,
  MONTH(msa.DATE) AS Month,
  SUM(msk.POINT) AS Ps,
  SUM(msa.VIDEO_SCORE) AS Video,
  SUM(msa.EXERCISESCORE) AS Lessons,
  SUM(msa.DS) AS DS
FROM student_p msk
RIGHT JOIN student_act msa ON msk.DATE = msa.DATE
GROUP BY  YEAR(msa.DATE), MONTH(msa.DATE)

student_p表:-

student_act表:-

预期结果:-

tktrz96b

tktrz96b1#

在第二个查询中,您将按年份限制结果集。如果这是您想要做的,则应该基于日期执行,而不是基于日期的函数,因为它是non-sargable。这样就可以使用Hossam建议的索引。(过于)简单地说,HAVING子句是在完成所有工作之后应用的,而WHERE子句减少了正在完成的工作量:

SELECT
  YEAR(msa.DATE) AS Year,
  MONTH(msa.DATE) AS Month,
  SUM(msk.POINT) AS Ps,
  SUM(msa.VIDEO_SCORE) AS Video,
  SUM(msa.EXERCISESCORE) AS Lessons,
  SUM(msa.DS) AS DS
FROM student_p msk
RIGHT JOIN student_act msa ON msk.DATE = msa.DATE
WHERE msa.DATE >= MAKEDATE(2023, 1) AND msa.DATE < MAKEDATE(2024, 1)
GROUP BY `Year`, `Month`;

MAKEDATE(年,一年中的某一天)
返回一个日期,给定年份和年中的日期值。dayofyear必须大于0,否则结果为NULL。
在你的一条评论中,你说你想要一个full join,MySQL没有,但是你可以用一个(LEFT|RIGHT) JOINUNION和另一个(LEFT|RIGHT) JOIN实现同样的目标。

WITH st (`Year`, `Month`, `Ps`) AS (
    SELECT
        YEAR(`P_date`) AS `Year`,
        MONTH(`P_date`) AS `Month`,
        SUM(`Points`)
    FROM `student_p`
    -- WHERE `P_date` >= MAKEDATE(2023, 1) AND `P_date` < MAKEDATE(2024, 1)
    GROUP BY `Year`, `Month`
),
act (`Year`, `Month`, `Video`, `Lessons`, `DS`) AS (
    SELECT
        YEAR(`A_date`) AS `Year`,
        MONTH(`A_date`) AS `Month`,
        SUM(`VIDEO_SCORE`) AS `Video`,
        SUM(`EXERCISESCORE`) AS `Lessons`,
        SUM(`DS`) AS `DS`
    FROM student_act
    -- WHERE A_date >= MAKEDATE(2023, 1) AND DATE < MAKEDATE(2024, 1)
    GROUP BY `Year`, `Month`
)
SELECT
    `Year`,
    `Month`,
    SUM(`Ps`) AS `Ps`,
    SUM(`Video`) AS `Video`,
    SUM(`Lessons`) AS `Lessons`,
    SUM(`DS`) AS `DS`
FROM (
    SELECT `act`.`Year`, `act`.`Month`, `Ps`, `Video`, `Lessons`, `DS`
    FROM `st`
    RIGHT JOIN `act` ON `st`.`Year` = `act`.`Year` AND `st`.`Month` = `act`.`Month`

    UNION ALL

    SELECT `st`.`Year`, `st`.`Month`, `Ps`, `Video`, `Lessons`, `DS`
    FROM `st`
    LEFT JOIN `act` ON `st`.`Year` = `act`.`Year` AND `st`.`Month` = `act`.`Month`
    WHERE `act`.`Year` IS NULL
) t
GROUP BY `Year`, `Month`
ORDER BY `Year` DESC, `Month` DESC;

输出:
| 年份|月份|Ps|视频|教训|DS|
| - ------|- ------|- ------|- ------|- ------|- ------|
| 二○二三|十一|四十五|二十二点四十三|二三三点四四五五|二十三|
| 二○二三|十个|零|二十四点五六六|二百三十二|三十四|
| 小行星2022|十个|零|四十六|六十八|六十六|
| 二〇一三年|十个|六十五|零|零|零|
两个CTE进行聚合。我保留了WHERE子句,但注解掉了。
UNION中的第一个查询是当前的RIGHT JOIN,它从右侧表中检索所有记录,无论它们在左手表中是否有关联行。第二个查询然后从左侧表中获取所有在右侧没有关联行的行。
下面是一个db<>fiddle,其中调整了无效日期和重复PK值。
这是你要找的吗?

kwvwclae

kwvwclae2#

对于初学者,您可以尝试index student_act表上的DATE字段。索引有助于SQL引擎更快地找到基于该字段的记录。
我的SQL:

ALTER TABLE `student_act` ADD INDEX `date_index` (`DATE`)

索引优化了SQL引擎中的查找过程,如果查询匹配基于MULTIPLE字段的记录,或者像您的示例中一样,匹配的字段是非数字的,则通常非常有效。
另外,尝试将字段重命名为不同于DATE的名称,因为它在某些SQL引擎中是保留字。

9fkzdhlc

9fkzdhlc3#

你也可以试试这个

SELECT
  YEAR(msa.DATE) AS Year,
  MONTH(msa.DATE) AS Month,
  SUM(msk.POINT) AS Ps,
  SUM(msa.VIDEO_SCORE) AS Video,
  SUM(msa.EXERCISESCORE) AS Lessons,
  SUM(msa.DS) AS DS
FROM student_p msk
RIGHT JOIN student_act msa ON msk.DATE = msa.DATE
WHERE msa.DATE between '2023-01-01 00:00:00' AND '2023-12-31 23:59:59'
GROUP BY MONTH(msa.DATE);
ygya80vv

ygya80vv4#

不要使用YEAR()和MONTH()函数,尝试按LAST_DAY()分组。它会给出包含任何DATE、DATETIME或TIMESTAMP的月份的最后一天。
就像这样:

SELECT
  LAST_DAY(msa.DATE) AS MonthEnding,
  SUM(msk.POINT) AS Ps,
  SUM(msa.VIDEO_SCORE) AS Video,
  SUM(msa.EXERCISESCORE) AS Lessons,
  SUM(msa.DS) AS DS
FROM student_p msk
RIGHT JOIN student_act msa ON msk.DATE = msa.DATE
WHERE msa.DATE >= MAKEDATE(2023, 1) AND msa.DATE < MAKEDATE(2024, 1)
GROUP BY LAST_DAY(msa.DATE);

student_p(date, POINT)student_act(DATE, VIDEO_SCORE, EXERCISES_SCORE, DS)上分别创建一个覆盖索引。
这种形式的查询和那些索引将有很大的帮助。

相关问题