如何简化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.createdDATETIMETIMESTAMP,你需要这个而不是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?的主要内容,如果未能解决你的问题,请参考以下文章

mysql中的union用法

MYSQL union 和union all 用法 /

MYSQL union 和union all 用法 /

mysql中的union和union all有啥区别? [复制]

Mysql联合查询UNION和UNION ALL的使用介绍

Mysql联合查询UNION和UNION ALL的使用介绍