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

标签 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从临时表中选择数据返回给客户端显示。

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

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

最佳答案

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

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. 其余部分只是行列表上的程序迭代的一种黑客表示(按索引顺序读取,按 location asc, visisted asc 排序):如果位置不同于前一行 - 我计算它,如果没有 - 我检查间隔,如果错误则返回 NULL。

关于php - MySQL:可以根据每条记录参数进行分组的 SELECT 条件语句吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10239290/

相关文章:

php - Mysql 查询排除当前显示的项目

sql - 在其他列中引用 postgresql 查询列

sql - Crystal 报表 - 上个月的最后一天

php - 带有 JOIN 的 MySQL 查询未正确执行

php - PHP 翻译

javascript - Nativescript http php 请求

php - 什么会更好 : click counter on mysql or on flat file?

mysql - 构建一个预订网站

php - 减少PHP中多维数组的下层

mysql - 寻址三个表 : How many from A are not in B or C? 的 MySQL 查询