我最近发布了一种新技术来处理 MySQL 中的此类问题。
标量聚合减少
Scalar-Aggregate Reduction 是迄今为止实现这一目标的最高性能方法和最简单的方法(在数据库引擎方面),因为它不需要连接、子查询和 CTE。
对于您的查询,它看起来像这样:
SELECT
video_category,
MAX(video_id) AS video_id,
SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS video_url,
SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_date)), 12) AS video_date,
SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_title)), 12) AS video_title,
SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), short_description)), 12) AS short_description
FROM
videos
GROUP BY
video_category
标量和聚合函数的组合执行以下操作:
- LPADs 聚合内相关标识符以允许正确的字符串比较(例如,“0009”和“0025”将被正确排列)。假设一个 INT 主键,我在这里 LPADDING 到 11 个字符。如果您使用 BIGINT,您将需要增加它以支持您的表的序数。如果您在 DATETIME 字段(固定长度)上进行比较,则不需要填充。
- CONCAT 使用输出列填充的标识符(因此您会得到“00000000009myvalue”与“0000000025othervalue”)
- MAX 聚合集,将产生“00000000025othervalue”作为获胜者。
- SUBSTRING 结果,这将截断比较的标识符部分,只留下值。
如果您想检索除 CHAR 以外的类型的值,您可能需要对输出执行额外的 CAST,例如如果您希望 video_date 成为日期时间:
CAST(SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_date)), 12) AS DATETIME)
与自连接方法相比,此方法的另一个好处是您可以组合其他聚合数据(不仅仅是最新值),甚至可以在同一查询中组合第一个和最后一个项目,例如
SELECT
-- Overall totals
video_category,
COUNT(1) AS videos_in_category,
DATEDIFF(MAX(video_date), MIN(video_date)) AS timespan,
-- Last video details
MAX(video_id) AS last_video_id,
SUBSTRING(MAX(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS last_video_url,
...
-- First video details
MIN(video_id) AS first_video_id,
SUBSTRING(MIN(CONCAT(LPAD(video_id, 11, '0'), video_url)), 12) AS first_video_url,
...
-- And so on
如需进一步详细说明此方法与其他旧方法相比的优势,请参阅我的完整博文:https://www.stevenmoseley.com/blog/tech/high-performance-sql-correlated-scalar-aggregate-reduction-queries