MySQL:SELECT条件语句可以根据每个记录参数进行分组吗?

时间:2012-04-20 02:04:59

标签: php mysql sql database

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

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

我将使用一个补充的场景来描述我想要做的事情:

让我们假设下表(请注意在实际应用中,此表的信息实际上是从几个表中提取的,但为了清晰起见,这应该是明确的):

+----+-----------+--------------+--------------+
| 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分钟。

第一个条目被自动锁定,因为之前没有条目,第二个条目也是第二个条目,因为“地方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从临时表中选择数据并将其返回给客户端进行显示。
  5. 我的问题是。有没有办法单独使用MySQL来完成大部分工作?我一直想找到的是一种编写MySQL查询的方法,该查询可以解析select语句,只选择符合上述条件的访问,然后最终按位置对其进行分组,并为我提供COUNT(*)每个小组。

    我真的不知道是否可能,并希望其中一位数据库大师可能能够阐明如何做到这一点。

2 个答案:

答案 0 :(得分: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');

如您所见,其中包含一个索引(locationvisited)。然后,以下查询将使用索引,即按索引顺序读取数据,并返回您期望的结果:

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。

答案 1 :(得分: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的想法。但是你可以只使用一个好的查询来完成报告。