2

我有一份我正在为使用 MySQL 作为数据库的应用程序重写的报告。目前,该报告正在使用来自 php 的大量繁重工作,它创建数组,将它们重新存储到临时数据库中,然后从该临时数据库生成结果。

重写所有这些代码的主要目标之一是简化和清理我的许多旧代码,我想知道是否可以简化以下过程,或者甚至更好地仅在 MySQL 上完成,让 php 只处理 dstribution数据到客户端。

我将使用一个虚构的场景来描述我正在尝试做的事情:

让我们假设下表(请注意,在实际应用中,该表的信息实际上是从多个表中提取的,但为了清楚起见,这应该能够说明问题):

+----+-----------+--------------+--------------+
| id | location  | date_visited | time_visited |
+----+-----------+--------------+--------------+
| 1  | place 1   | 2012-04-20   | 11:00:00     |
+----+-----------+--------------+--------------+
| 2  | place 2   | 2012-04-20   | 11:06:00     |
+----+-----------+--------------+--------------+
| 3  | place 1   | 2012-04-20   | 11:06:00     |
+----+-----------+--------------+--------------+
| 4  | place 3   | 2012-04-20   | 11:20:00     |
+----+-----------+--------------+--------------+
| 5  | place 2   | 2012-04-20   | 11:21:00     |
+----+-----------+--------------+--------------+
| 6  | place 1   | 2012-04-20   | 11:22:00     |
+----+-----------+--------------+--------------+
| 7  | place 3   | 2012-04-20   | 11:23:00     |
+----+-----------+--------------+--------------+

我需要的报告要求我首先列出每个地点,然后列出对该地点的访问次数。但是,需要注意的是,使查询变得困难的原因是,访问需要有一个时间间隔才能计入此报告。

例如:假设访问任何给定地点的间隔是 10 分钟。

第一个条目被自动锁定,因为没有以前的条目,第二个条目也是如此,因为“place 2”还没有其他条目。但是,在第三个条目上,会检查地点 1 的最后一次访问时间,该时间小于定义的时间间隔(10 分钟),因此报告将忽略此条目并继续下一个条目。

本质上,我们正在逐案检查时间间隔不是从最后一个条目开始,而是从同一位置的最后一个条目开始。

报告的结果最终应如下所示:

+----+-----------+--------+
| id | location  | visits |
+----+-----------+--------+
| 1  | place 1   | 2      |
+----+-----------+--------+
| 2  | place 2   | 2      |
+----+-----------+--------+
| 3  | place 3   | 1      |
+----+-----------+--------+

我当前的基本实现通过以下步骤来获取上述结果集:

  1. MySQL 查询创建一个临时表,其中包含所有必需位置及其 ID 的列表。
  2. MySQL查询选择指定时间范围内的所有访问数据并将其传递给PHP。
  3. PHP & MySQL 用访问数据填充临时表,PHP 在这里完成繁重的工作。
  4. MySQL 从临时表中选择数据并将其返回给客户端进行显示。

我的问题是。有没有办法单独使用 MySQL 来完成大部分工作?我一直在寻找的是一种编写 MySQL 查询的方法,该查询可以通过 select 语句解析并仅选择满足上述条件的访问,然后最终按位置对其进行分组并为我提供 COUNT(*)每组。

我真的不知道这是否可能,并且希望那里的一位数据库专家能够阐明如何做到这一点。

4

2 回答 2

2

假设您有一个结构略有不同的表(可能是临时表):

CREATE TABLE  `visits` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `location` varchar(45) NOT NULL,
  `visited` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `loc_vis` (`location`,`visited`)
) ENGINE=InnoDB;

INSERT INTO visits (location, visited) VALUES
('place 1', '2012-04-20 11:00:00'),
('place 2', '2012-04-20 11:06:00'),
('place 1', '2012-04-20 11:06:00'),
('place 3', '2012-04-20 11:20:00'),
('place 2', '2012-04-20 11:21:00'),
('place 1', '2012-04-20 11:22:00'),
('place 1', '2012-04-20 11:23:00');

如您所见,它在 ( location, visited) 上有一个索引。那么下面的查询会使用索引,也就是按照索引的顺序读取数据,并返回你期望的结果:

SELECT
  location,
  COUNT(IF(@loc <> @loc:=location,
           @vis:=visited,
           IF(@vis + INTERVAL 10 MINUTE < @vis:=visited,
              visited,
              NULL))) as visit_count
FROM visits,
     (SELECT @loc:='', @vis:=FROM_UNIXTIME(0)) as init
GROUP BY location;

结果:

+----------+-------------+
| location | visit_count |
+----------+-------------+
| place 1  |           2 |
| place 2  |           2 |
| place 3  |           1 |
+----------+-------------+
3 rows in set (0.00 sec)

一些解释

解决方案的关键是它淡化了 SQL 的功能特性,并使用 MySQL 实现细节(他们说这很糟糕,永远不要再这样做了!!!)。

  1. 如果表有索引(列值的有序表示)并且在查询中使用了索引,则意味着表中的数据按索引的顺序读取。

  2. GROUP BY 操作将受益于索引(因为数据已经在那里分组),如果适用,将选择它。

  3. SQL 中的所有聚合函数(COUNT(*)具有特殊含义的除外)都会检查每一行,只有当它不为 NULL 时才使用该值(上面 COUNT 中的表达式在错误条件下返回 NULL)

  4. 其余的只是对行列表的程序迭代的一个hacky表示(按索引的顺序读取,按 排序location asc, visisted asc):我初始化一些变量,如果位置与前一行不同 - 我计算它,如果不是 - 我检查间隔,如果错误则返回 NULL。

于 2012-04-20T05:34:31.147 回答
0

您可以使用 INSERT / SELECT 语句填充临时表。

见手册。http://dev.mysql.com/doc/refman/5.0/en/insert-select.html

我会在 SELECT 语句中使用 GROUP BY 来缩小范围。

对于可以作为 COUNT 操作填充的访问列,我认为它也可以作为 INSERT / SELECT 的一部分执行。

见手册。http://dev.mysql.com/doc/refman/5.1/en/counting-rows.html

所以你的 SQL 可能看起来像这样。

INSERT INTO temp 
    SELECT * FROM (
        SELECT *,COUNT('visits') 
             FROM source AS table1 
             GROUP BY location
             WHERE date_visited > xxxx AND date_visited < xxxx
        )
       AS table2

说真的,这不是我的想法,但它应该会给你一些关于如何构建 SQL 的想法。但是您可能只使用一个好的查询就可以完成报告。

于 2012-04-20T02:21:00.007 回答