如何简化mysql中的多个UNION ALL?
Posted
技术标签:
【中文标题】如何简化mysql中的多个UNION ALL?【英文标题】:How to simplify multiple UNION ALL in mysql? 【发布时间】:2021-02-22 16:58:23 【问题描述】:由于多个 UNION ALL,我的查询变得非常庞大,任何人都可以帮我减少它。实际上有 300 多个 UNION ALL。
SELECT keywords,
COUNT(i.postId) as Posts,
SUM(i.interactions) as Interactions,
GROUP_CONCAT(DISTINCT i.users) as Users
FROM (
SELECT 'keyword1' as keywords,
`postId`,
`interactions`,
( SELECT displayName FROM profile WHERE id=userID LIMIT 1 ) as users
FROM `posts`
WHERE `content` REGEXP 'keyword1'
AND created BETWEEN '2021-01-01' AND '2021-02-19'
AND userID IN (147483,166451,166467)
UNION ALL
SELECT 'keyword2' as keywords,
`postId`,
`interactions`,
( SELECT displayName FROM profile WHERE id=userID LIMIT 1 ) as users
FROM `posts`
WHERE `content` REGEXP 'keyword2'
AND created BETWEEN '2021-01-01' AND '2021-02-19'
AND userID IN (147483,166451,166467)
UNION ALL
SELECT 'keyword3' as keywords,
`postId`,
`interactions`,
( SELECT displayName FROM profile WHERE id=userID LIMIT 1 ) as users
FROM `posts`
WHERE `content` REGEXP 'keyword3'
AND created BETWEEN '2021-01-01' AND '2021-02-19'
AND userID IN (147483,166451,166467)
) i
GROUP BY keywords
【问题讨论】:
修复您的数据模型,以便将值存储在行中而不是列中。 一个糟糕的数据结构可以对您的代码造成什么影响的完美示例。select version();
显示什么?
我不理解这些批评;这似乎不是行而不是列的情况;这只是尝试匹配多个关键字并报告匹配的关键字和匹配频率。 @GordonLinoff 你能解释一下吗?也许我只是忽略了你所看到的一切
您能向我们展示您的数据模型并解释您想用这个查询做什么吗?
【参考方案1】:
在这种情况下,记住 SQL 是一种声明性而不是过程语言会有所帮助。你描述你想要什么。
您似乎有一个包含 300 个关键字的列表,您希望使用它来总结您的 content
列。
假设您将这些关键字存储在名为keywords
的表中。
并且,假设您的profile
表的主键是id
,匹配posts.userId
。这意味着我们可以通过 JOIN 而不是一堆子查询来检索用户的 displayName
值。
然后你可以这样写你的查询。我们将从这个子查询开始检索与您的关键字匹配的行的详细信息。
SELECT keywords.keyword,
posts.content, posts.postId,
posts.interactions,
profile.displayName
FROM posts
JOIN profile ON posts.userId = profile.id
JOIN keywords ON post.content RLIKE keywords.keyword
WHERE posts.created BETWEEN '2021-01-01' AND '2021-02-19'
AND posts.userID in (147483,166451,166467)
这会生成一个虚拟表,其中包含您要汇总的帖子。您应该对此进行测试并说服自己它是正确的。
然后你通过改变查询的 SELECT 并添加一个 GROUP BY 来总结它们,就像这样。
SELECT keywords.keyword,
COUNT(*) count,
SUM(posts.interactions) interactions,
GROUP_CONCAT(DISTINCT profile.displayName ORDER BY profile.displayName) users
FROM posts
JOIN profile ON posts.userId = profile.id
JOIN keywords ON post.content RLIKE keywords.keyword
WHERE posts.created BETWEEN '2021-01-01' AND '2021-02-19'
AND posts.userID in (147483,166451,166467)
GROUP BY keywords.keyword;
你就完成了。将关键字列表移动到它们自己的表中是摆脱庞大的 UNION ALL 级联的秘诀。
您可能会发现这一行比使用正则表达式要快一些。
JOIN keywords ON post.content LIKE CONCAT('%', keywords.keyword, '%')
最后,如果posts.created
是DATETIME
或TIMESTAMP
,你需要这个而不是created BETWEEN
,这样你就可以得到你范围的最后一天的所有项目。注意<
表示范围的结尾。
WHERE posts.created >= '2021-01-01'
AND posts.created < '2021-02-19' + INTERVAL 1 DAY
您需要将此标准用于日期范围的结束,因为日期常量2021-02-19
实际上表示2021-02-19 00:00:00
,或该日期开始时的午夜。例如,2021-02-19 10:22:00
的时间戳在那个午夜之后,所以 BETWEEN 不会做你想做的事。
【讨论】:
嗨,O.琼斯。我真的很喜欢您在回答中提供的详细信息并给了+1。但是,我一直认为 between 包含了开始值和结束值。这不正确吗? 请阅读我在答案末尾添加的段落。 谢谢!我以前从未发现过这种情况,并且一直认为结束日期更像是 left(date_field,10)。以上是关于如何简化mysql中的多个UNION ALL?的主要内容,如果未能解决你的问题,请参考以下文章