【问题标题】:Mysql - SQL queries for counter web visits per day, month, year and totalsMysql - 每天、每月、每年和总计的计数器 Web 访问的 SQL 查询
【发布时间】:2016-09-01 16:55:11
【问题描述】:

我正在制作一个系统来计算我的网站的访问次数,然后将其显示在图表类型上。

例如,我想通过以下方式获得所有访问:

  1. 今天的网络访问者总数

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE DATE_FORMAT(create_at, '%Y-%m-%d') = CURDATE()) AS total_today

  1. 一小时内的网络总访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS total_before_hours

  1. 昨天的总访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS total_yesterday

  1. 本周网站总访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE YEARWEEK(create_at, 1) = YEARWEEK(CURDATE(), 1)) AS total_week

  1. 上周网站总访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)) AS total_last_weekend

  1. 每月总网站访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE MONTH(create_at) = MONTH(NOW())) AS total_month

  1. 上个月网站总访问量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS total_last_month

  1. 全年网络访问总量

SELECT COUNT(DISTINCT ip) FROM visits_website WHERE YEAR(create_at) = YEAR(CURDATE())) AS total_year

我在 MySql 表中注册访问该网站,并且我想获取此表在指定时间段内收到的所有访问不同 IP 的 Web,我已经与函数 MySql [DATE_SUB] 进行了几次协商,因为必须更改几次查询:

这些是我对所有访问进行的查询:

SELECT 
COUNT(DISTINCT ip) AS total,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at  >= DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS total_before_hours,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE DATE_FORMAT(create_at, '%Y-%m-%d') = CURDATE()) AS total_today,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at  >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS total_yesterday,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE YEARWEEK(`create_at`, 1) = YEARWEEK(CURDATE(), 1)) AS total_week,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at  >= DATE_SUB(NOW(), INTERVAL 1 WEEK)) AS total_last_weekend,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE MONTH(`create_at`) = MONTH(NOW())) AS total_month,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE create_at  >= DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS total_last_month,
(SELECT COUNT(DISTINCT ip) FROM visits_website WHERE YEAR(`create_at`) = YEAR(CURDATE())) AS total_year
FROM visits_website

我想了解以下内容:

  1. 是这些优化的mysql查询和制作这个系统的最佳方式吗?
  2. 这是一个很好的开发实践,表中需要更多的索引。

表格代码:

CREATE TABLE IF NOT EXISTS `visits_website` (
    `id` BIGINT UNSIGNED AUTO_INCREMENT,
    `ip` VARCHAR(25) NOT NULL,
    `browser_short` VARCHAR(45) NOT NULL,
    `browser_long` VARCHAR(255) NOT NULL,
    `create_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `FK_visits_website` (`ip`)
)Engine=InnoDB;

INSERT INTO `visits_website` VALUES ('1', 'ip1', 'ip1', '', '2016-08-31 20:30:00');
INSERT INTO `visits_website` VALUES ('2', 'ip1', 'ip1', '', '2016-08-31 20:30:00');
INSERT INTO `visits_website` VALUES ('3', 'ip2', 'ip2', '', '2016-08-31 19:30:00');
INSERT INTO `visits_website` VALUES ('4', 'ip1', 'ip1', '', '2016-08-31 19:30:00');
INSERT INTO `visits_website` VALUES ('5', 'ip2', 'ip2', '', '2016-08-31 18:30:00');
INSERT INTO `visits_website` VALUES ('6', 'ip3', 'ip3', '', '2016-08-31 18:30:00');
INSERT INTO `visits_website` VALUES ('7', 'ip1', 'ip1', '', '2016-08-31 17:30:00');
INSERT INTO `visits_website` VALUES ('8', 'ip2', 'ip2', '', '2016-08-31 17:30:00');
INSERT INTO `visits_website` VALUES ('9', 'ip3', 'ip3', '', '2016-08-31 16:30:00');
INSERT INTO `visits_website` VALUES ('10', 'ip4', 'ip4', '', '2016-08-31 16:30:00');
INSERT INTO `visits_website` VALUES ('11', 'ip1', 'ip1', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('12', 'ip2', 'ip2', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('13', 'ip3', 'ip3', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('14', 'ip4', 'ip4', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('15', 'ip5', 'ip5', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('16', 'ip1', 'ip1', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('17', 'ip2', 'ip2', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('18', 'ip3', 'ip3', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('19', 'ip4', 'ip4', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('20', 'ip5', 'ip5', '', '2016-08-30 20:30:00');
INSERT INTO `visits_website` VALUES ('21', 'ip6', 'ip6', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('22', 'ip1', 'ip1', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('23', 'ip2', 'ip2', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('24', 'ip3', 'ip3', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('25', 'ip4', 'ip4', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('26', 'ip5', 'ip5', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('27', 'ip6', 'ip6', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('28', 'ip7', 'ip7', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('29', 'ip1', 'ip1', '', '2016-08-29 20:30:00');
INSERT INTO `visits_website` VALUES ('30', 'ip2', 'ip2', '', '2016-08-29 20:30:00');

非常感谢您的帮助。

【问题讨论】:

  • 为当年访问的所有用户运行查询似乎有点压倒性。因此,应该有一些自动化的 crono 任务来构建另一个表来浓缩信息并减少所需的处理量。如果您想保留查询,可以每 24 小时运行一次并缓存其中一些查询,以减少在每次页面展示后运行它们的需要。
  • 如果你有这个想法,同时也是一个公共区域的部分,只为一个经理,因为我想做这样的事情jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/…,我正在审查这个网站plumislandmedia.net/mysql/sql-reporting-time-intervals,这样做是最合适的方法,但是有很多方法,我在做这些咨询的这种方式,可以被认为是一种良好的实践发展。

标签: php mysql database


【解决方案1】:

这是一个很长的问题。关于这种基于DATETIME 的摘要生成的一个关键建议:Make your queries sargable -- make them able to use an index.

例如:

 SELECT COUNT(DISTINCT ip)
   FROM visits_website
  WHERE MONTH(create_at) = MONTH(NOW()  /* Slow! */

不是 sargable,因为它将函数 (MONTH()) 应用于表中的列。 MySQL 将不得不检查表的每一行 以满足该查询。那将是slooooow。而是试试这个,以查找本月的所有访问。

 SELECT COUNT(DISTINCT ip)
   FROM visits_website
  WHERE create_at >= LAST_DAY(NOW()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
    AND create_at < LAST_DAY(NOW()) + INTERVAL 1 DAY

之所以有效,是因为它会搜索从本月初到但不包括下月初的一系列 DATETIME 值。

然后,在(create_at, ip) 上创建一个compound covering index,您的查询应该可以正常工作。 MySQL 可以扫描它需要的索引范围。

请注意,这一切都适用于 TIMESTAMP 数据。

【讨论】:

  • 亲爱的琼斯,:) 如果这是一个很长的问题,非常感谢您的帮助,对我帮助很大,您所说的非常合乎逻辑,祝您有美好的一天。跨度>
  • 我看到了函数 LAST_DAY() 的东西,如果一个月有 30 天,通过添加一天 LAST_DAY(NOW()) - INTERVAL 1 MONTH + INTERVAL 1 DAY,你应该去下一个月,但加 1 到 30,得到 31,加一天的想法是下个月,因为我们可以解决这个问题,所以通过在有 30 天的一个月中添加一天,mysql 检测到该月只有 30天,并且将在下个月添加而不是向 30 添加值 Mysql 可能会检测到吗?
  • 你是对的。我编辑了。 - INTERVAL 1 MONTH 仅移动到上个月的第一天,如果它已经在一个月的第一天。 LAST_DAY('2016-02-27') + INTERVAL 1 DAY - INTERVAL 1 MONTH 得到2016-02-01,但LAST_DAY('2016-02-27') - INTERVAL 1 MONTH + INTERVAL 1 DAY 得到2016-01-30,这是错误的。
  • 我需要从数据库中选择所有的网络访问,例如上周、上个月、最后一小时,为此我有两种方法可以做到这一点,一种是在数据库中添加一个额外的字段将日期存储为日期('Y-m-d'),然后准确地在上周使用 NOW('Y-m-d H:i:s'),选择考虑小时、分钟和秒。我可以这样做,但是必须为日期字段创建另一个索引,这会不会是一个困境优化索引放插入查询,更新,当用户访问网站的前端慢时记录网络访问。跨度>
  • 在所有情况下,您都可以使用索引范围过滤。你只需要计算你想要的开始和结束DATETIME,然后像我的回答一样使用WHERE 子句。可能不需要具有四舍五入的 DATETIME 值的额外列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多