【问题标题】:Mysql 3 tables, multiple counts grouped by, but gets stuckMysql 3个表,多个计数分组,但卡住了
【发布时间】:2018-12-01 22:18:28
【问题描述】:

我在查询时遇到了一些麻烦。我正在尝试检索涉及 3 个表的大型数据库的一些数据。

这些表包含有关添加的数据,在后端网站中,管理员可以管理他想要显示的本地添加、位置等...这些被组织在 3 个表中,其中 1 个包含所有数据与添加信息(名称、可用日期、到期日期等)相关的信息。然后,还有另外 2 个表格,其中包含一些额外的信息,但只是关于视图或点击。

所以我只有 15 个添加,它们具有多次点击和多次查看。 每次点击并查看表格,每次点击都会注册一个新行。因此,当点击注册时,它会添加一个新行,其中 addid_views 是一个注册(点击),并且 addid 是 addid 来自 add_table。例如,添加 (1) 将有 2 次查看和 2 次点击,而添加 (2) 将有 1 次查看和 1 次点击。

我的想法是获取每次添加的总点击次数和查看次数。

我有 3 个这样的表:

         adds_table           adds_clicks_table        adds_views_table
    +-------+-----------+   +-------------+------+     +-------------+------+  
    | addid | name      |   | addid_click |addid |     | addid_views |addid |
    +-------+-----------+   +-------------+------+     +-------------+------+
    | 1     | add_name1 |   | 1           | 1    |     | 1           | 1    |
    +-------+-----------+   +-------------+------+     +-------------+------+
    | 2     | add_name2 |   | 2           | 2    |     | 2           | 1    |
    +-------+-----------+
    | 3     | add_name3 |   | 3           | 1    |     | 3           | 2    |
    +-------+-----------+   +-------------+------+     +-------------+------+


CREATE TABLE `bwm_adds` (
  `addid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  ...
  PRIMARY KEY (`addid`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8

CREATE TABLE `bwm_adds_clicks` (
  `add_clickid` int(19) NOT NULL AUTO_INCREMENT,
  `addid` int(11) NOT NULL,
  ...
  PRIMARY KEY (`add_clickid`)
) ENGINE=InnoDB AUTO_INCREMENT=3374 DEFAULT CHARSET=utf8


CREATE TABLE `bwm_adds_views` (
  `add_viewsid` int(19) NOT NULL AUTO_INCREMENT,
  `addid` int(11) NOT NULL,
  ...
  PRIMARY KEY (`add_viewsid`)
) ENGINE=InnoDB AUTO_INCREMENT=2078738 DEFAULT CHARSET=utf8

结果将是我检索到的单个表格,每个添加 (addid),点击次数和查看次数。

我需要得到一个查询,我得到这样的东西:

+-------+---------+-----------+
| addid | clicks  | views     |
+-------+---------+-----------+
| 1     | 123123  | 235457568 |
+-------+---------+-----------+
| 2     | 5124123 | 435345234 |
+-------+---------+-----------+
| 3     | 123541  | 453563623 |
+-------+---------+-----------+

我尝试执行一个查询,但它卡住并加载了未定义的时间...我很确定我的查询失败了,因为如果我删除其中一个计数,会非常快地显示一些数据。

SELECT a.addid, COUNT(ac.addid_clicks) as 'clicks', COUNT(av.addid_views) as 'views'
        FROM `adds_table` a 
        LEFT JOIN `adds_clicks_table` ac ON a.addid = ac.addid_click
        LEFT JOIN `adds_views_table` av ON ac.addid_click = av.addid_views
                GROUP BY a.addid

Mysql 一直在加载,有什么想法可以帮助了解我缺少什么吗?

顺便说一下,我发现 this post where 处理的问题几乎与我遇到的相同,您可以看到我的查询与第一个答案非常相似,但我收到了正在加载的消息时间。没有错误,正在加载中。

编辑:我把数字放错了,弄错了。现在表格是固定的,我添加了一些关于它的解释。

Edit2:我用 SHOW CREATE TABLES DEFINITIONS 更新了帖子。

Edit3:有没有办法优化这个查询?似乎它检索到了我想要的结果,但是 mysql 数据库取消了查询,因为它的执行时间超过了 30 秒。

SELECT a.addid,
       (SELECT COUNT(addid) FROM add_clicks where addid =  a.addid) as clicks,
       (SELECT COUNT(addid) FROM add_views  where addid =  a.addid) as views
    FROM adds a ORDER BY a.addid;

【问题讨论】:

  • 您没有提供足够的信息让我们帮助您。请read this note about asking good SQL questions,并关注查询性能部分。那么请edit你的问题。
  • 抱歉,我在做表格时弄糊涂了。我希望现在它更容易理解了。
  • 您使用的是什么版本的 MySQL? (对查询的一项改进在旧版本中效果不佳。)
  • 我们仍然需要查看带有索引的表定义来帮助您。
  • 使用Mysql 5.6.38。我将使用 3 个 CREATE TABLE 定义更新帖子。

标签: mysql group-by count query-performance


【解决方案1】:

如果这些确实是你的表(一列,加上一个 auto_inc),那么没有有意义的信息证明有 3 个表而不是 1 个:

CREATE TABLE `bwm_adds` (
  `addid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  clicks INT UNSIGNED NOT NULL,
  views  INT UNSIGNED NOT NULL,
  PRIMARY KEY (`addid`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8

然后UPDATE ... SET views = views + 1(等),而不是插入到其他表中。

【讨论】:

  • Rick,我简化了其中的 3 个以忽略插入的不相关元数据。像删除的参数、日期和其他与我的查询无关的东西。它们都不包含外键,我所展示的只是获取查询所必需的。我确实同意这个数据库是错误构建的,我会在一张表中完成所有工作,但是由于这已经是一个拥有庞大数据库的大型项目并且它是这样构建的,因此使用其他结构更改数据库(即使它只是一张表)。
  • @LukeMarks - 将经常更新的内容(如点赞和查看)与项目的主要信息分开通常是明智的(对于性能而言)。
  • 我才意识到那段写得多么糟糕。我想说的是,它已经建立的数据库和一个在不同文件上使用它的大型网站......也许有数百个。更改数据库意味着让一切重新运行并重建后端。这将导致时间的损失。也许如果所有者想更新网站,我会清理并做得更好。另外,我同意你的看法,再次为我的英语感到抱歉。
【解决方案2】:

如果你有旧版本,

        SELECT  a.addid, 
                ( SELECT  COUNT(addid_clicks)
                    FROM  `adds_clicks_table`
                    WHERE  addid = a.addid 
                ) AS 'clicks', 
                ( SELECT  COUNT(addid_clicks)
                    FROM  `adds_views_table`
                    WHERE  addid = a.addid 
                ) AS 'views'
            FROM  adds_table AS a

对于 5.6 及更高版本,这可能会更快:

SELECT  a.addid, c.clicks, v.views
    FROM  `adds_table` a
    LEFT JOIN ( SELECT addid, COUNT(addid_clicks) FROM addid_clicks ) AS c USING(addid)
    LEFT JOIN ( SELECT addid, COUNT(addid_views)  FROM addid_views )  AS v USING(addid)

如果您得到NULLs 但更喜欢0s,则将值包装在IFNULL(..., 0) 中。

如需进一步讨论,请提供SHOW CREATE TABLEEXPLAIN SELECT ...

【讨论】:

  • ...id, COUNT(addid_clicks) clicks FROM addid... ?摘要子查询可能需要别名。
  • @O.Jones - 简单规则:在子查询中,假定任何非别名名称属于子查询中的表。遵循此规则有助于轻松查看它是否“相关”。
  • 我在我的数据库上尝试了您的查询。这不是我想要的。我需要获得,每次添加,有多少视图和点击有添加。所以它会像 add1 clicks = 120 views = 123 add2 clicks = 1241 views = 12412 add3 clicks = 64 views = 7456 和你一样结束,但我得到了所有的 addid,但在第一个上,所有视图和点击总数,其余为空。
【解决方案3】:

我以解决我的问题结束。我试图访问的表是糟糕的工程数据库的主要原因,在 add_views_table 中,对于每个视图,都会添加一个新行。以近 300 万行和一个占整个数据库 (326MB) 近 35% 的表结束。

当 phpmyadmin 尝试执行查询时,由于 mysql 应用了超时限制,因此永远加载并且从未显示结果。更改此值将有助于检索该数据并将其显示在网站上但不可行(这意味着在执行查询之前不会加载网站或数据)。

由于在add_table 中创建了addid 索引,这个问题得到了解决。此外,如果出于某种原因使用子查询,查询会更快。查询结束如下:

SELECT a.addid,
(SELECT COUNT(addid) FROM adds_clicks_table WHERE addid = a.addid) AS 'clicks',(SELECT COUNT(addid) FROM adds_views_table WHERE addid = a.addid) AS 'views'
FROM adds_table a
ORDER BY a.addid;

感谢@Rick James 发布了类似的查询,我最终修改它以获得我需要的数据

原谅我糟糕的英语

【讨论】:

    猜你喜欢
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多