Par*_*roX 16 php mysql sql average date
假设一个表如:
UID Name Datetime Users
4 Room 4 2012-08-03 14:00:00 3
2 Room 2 2012-08-03 14:00:00 3
3 Room 3 2012-08-03 14:00:00 1
1 Room 1 2012-08-03 14:00:00 2
3 Room 3 2012-08-03 14:15:00 1
2 Room 2 2012-08-03 14:15:00 4
1 Room 1 2012-08-03 14:15:00 3
1 Room 1 2012-08-03 14:30:00 6
1 Room 1 2012-08-03 14:45:00 3
2 Room 2 2012-08-03 14:45:00 7
3 Room 3 2012-08-03 14:45:00 8
4 Room 4 2012-08-03 14:45:00 4
Run Code Online (Sandbox Code Playgroud)
我希望从下午2点到下午3点获得每个房间(1,2,3,4)的平均用户数.问题是有时房间可能不会在15分钟的间隔时间"登记",因此必须假设前一个最后已知的用户计数仍然有效.
例如,2012-08-03 14:15:00
4号房间的办理登机手续从未办理登机手续,因此必须假设房间4有3个用户,2012-08-03 14:15:00
因为这就是它所拥有的2012-08-03 14:00:00
接下来是这样,我正在寻找的平均用户数如下:
房间1:(2 + 3 + 6 + 3)/ 4 = 3.5
房间2:(3 + 4 + 4
+ 7)/ 4 = 4.5
房间3:(1 + 1 + 1
+ 8)/ 4 = 2.75
房间4 :( 3 + 3
+ 3
+ 4)/ 4 = 3.25
哪里#
是基于先前已知登记的假定号码.
我想知道是否有可能单独使用SQL?如果不是,我很好奇一个巧妙的PHP解决方案,不仅仅是暴力数学,例如我的快速不准确的伪代码:
foreach ($rooms_id_array as $room_id) {
$SQL = "SELECT * FROM `table` WHERE (`UID` == $room_id && `Datetime` >= 2012-08-03 14:00:00 && `Datetime` <= 2012-08-03 15:00:00)";
$result = query($SQL);
if ( count($result) < 4 ) {
// go through each date and find what is missing, and then go to previous date and use that instead
} else {
foreach ($result)
$sum += $result;
$avg = $sum / 4;
}
}
Run Code Online (Sandbox Code Playgroud)
你的困难(最昂贵的一步)将填补空白.如果无法在源数据中"填充空白",则可能需要使用模板加入,然后使用相关子查询来查找与该模板关联的数据.
对于真实的表格,这通常是最好的,但这里有一个硬编码的内嵌视图的例子......
SELECT
`room`.`uid` `uid` ,
AVG(`data`.`users`) `average_users`
FROM
(SELECT 1 `UID` UNION ALL
SELECT 2 `UID` UNION ALL
SELECT 3 `UID` UNION ALL
SELECT 4 `UID`) `room`
CROSS JOIN
(SELECT '2012-08-03 14:00:00' `datetime` UNION ALL
SELECT '2012-08-03 14:15:00' `datetime` UNION ALL
SELECT '2012-08-03 14:30:00' `datetime` UNION ALL
SELECT '2012-08-03 14:45:00' `datetime`) `checkin`
LEFT JOIN
data
ON `data`.`uid` = `room`.`uid`
AND `data`.`datetime` = (SELECT MAX(`datetime`)
FROM `data`
WHERE `uid` = `room`.`uid`
AND `datetime` <= `checkin`.`datetime`)
GROUP BY
`room`.`uid`
Run Code Online (Sandbox Code Playgroud)
- CROSS JOIN
创建模板以确保您始终拥有每个房间的每个签入位置的记录.
- correlated sub-query
搜索时间以查找当时该房间的最新签到.
您可以使用此解决方案:
SELECT b.Name,
AVG(b.Users) avg_users
FROM (
SELECT a.UID,
MAX(c.Datetime) last_date
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
JOIN tbl c ON a.UID = c.UID
AND TIME(b.intrvl) >= TIME(c.Datetime)
GROUP BY a.UID,
b.intrvl
) a
JOIN tbl b ON a.UID = b.UID
AND a.last_date = b.Datetime
GROUP BY b.UID,
b.Name
Run Code Online (Sandbox Code Playgroud)
我们需要做的第一件事是将每个房间与每个时间间隔相关联.例如,在您的示例数据,Room 4
不具有间隔关联14:15:00
和14:30:00
,但我们仍然需要以某种方式表示这些关联.
我们通过创建具有相关时间间隔的每个不同房间的笛卡尔积来实现这一目标:
SELECT a.UID,
b.intrvl
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
ORDER BY b.intrvl, a.UID DESC --Ordering for display purposes
Run Code Online (Sandbox Code Playgroud)
呈现:
UID | intrvl
--------------
4 | 14:00:00
3 | 14:00:00
2 | 14:00:00
1 | 14:00:00
4 | 14:15:00
3 | 14:15:00
2 | 14:15:00
1 | 14:15:00
4 | 14:30:00
3 | 14:30:00
2 | 14:30:00
1 | 14:30:00
4 | 14:45:00
3 | 14:45:00
2 | 14:45:00
1 | 14:45:00
Run Code Online (Sandbox Code Playgroud)
然后,一旦我们有了这些关联,我们就把结果重新加到主表(tbl
)上,条件是主表的Datetime
字段的时间部分小于每个字段的笛卡尔连接时间UID
.这将是每个UID
- > intrvl
关联,它将显示在intrvl
时间或之前发生的所有条目.
因此,例如,因为Room 3
没有为一个条目14:30:00
INTRVL,只有两个项目将加入与INTRVL:的那些上14:15:00
和14:00:00
因为它们都发生无论是在或INTRVL时间之前.
您现在可以看到我们的目标.此步骤的结果将使我们能够访问每个intrvl的最新条目.
SELECT a.UID,
b.intrvl,
c.*
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
JOIN tbl c ON a.UID = c.UID
AND TIME(b.intrvl) >= TIME(c.Datetime)
ORDER BY b.intrvl, a.UID DESC, c.Datetime --Ordering for display purposes
Run Code Online (Sandbox Code Playgroud)
渲染(不包括Name
列):
UID | intrvl | Datetime | Users
---------------- --------------------------------
4 | 14:00:00 | 2012-08-03 14:00:00 | 3 <-- Most recent entry up until 14:00:00
3 | 14:00:00 | 2012-08-03 14:00:00 | 1 <-- Most recent entry up until 14:00:00
2 | 14:00:00 | 2012-08-03 14:00:00 | 3 <-- Most recent entry up until 14:00:00
1 | 14:00:00 | 2012-08-03 14:00:00 | 2 <-- Most recent entry up until 14:00:00
4 | 14:15:00 | 2012-08-03 14:00:00 | 3 <-- Most recent entry up until 14:15:00
3 | 14:15:00 | 2012-08-03 14:00:00 | 1
3 | 14:15:00 | 2012-08-03 14:15:00 | 1 <-- Most recent entry up until 14:15:00
2 | 14:15:00 | 2012-08-03 14:00:00 | 3
2 | 14:15:00 | 2012-08-03 14:15:00 | 4 <-- Most recent entry up until 14:15:00
1 | 14:15:00 | 2012-08-03 14:00:00 | 2
1 | 14:15:00 | 2012-08-03 14:15:00 | 3 <-- Most recent entry up until 14:15:00
4 | 14:30:00 | 2012-08-03 14:00:00 | 3 <-- Most recent entry up until 14:30:00
3 | 14:30:00 | 2012-08-03 14:00:00 | 1
3 | 14:30:00 | 2012-08-03 14:15:00 | 1 <-- Most recent entry up until 14:30:00
2 | 14:30:00 | 2012-08-03 14:00:00 | 3
2 | 14:30:00 | 2012-08-03 14:15:00 | 4 <-- Most recent entry up until 14:30:00
1 | 14:30:00 | 2012-08-03 14:00:00 | 2
1 | 14:30:00 | 2012-08-03 14:15:00 | 3
1 | 14:30:00 | 2012-08-03 14:30:00 | 6 <-- Most recent entry up until 14:30:00
4 | 14:45:00 | 2012-08-03 14:00:00 | 3
4 | 14:45:00 | 2012-08-03 14:45:00 | 4 <-- Most recent entry up until 14:45:00
3 | 14:45:00 | 2012-08-03 14:00:00 | 1
3 | 14:45:00 | 2012-08-03 14:15:00 | 1
3 | 14:45:00 | 2012-08-03 14:45:00 | 8 <-- Most recent entry up until 14:45:00
2 | 14:45:00 | 2012-08-03 14:00:00 | 3
2 | 14:45:00 | 2012-08-03 14:15:00 | 4
2 | 14:45:00 | 2012-08-03 14:45:00 | 7 <-- Most recent entry up until 14:45:00
1 | 14:45:00 | 2012-08-03 14:00:00 | 2
1 | 14:45:00 | 2012-08-03 14:15:00 | 3
1 | 14:45:00 | 2012-08-03 14:30:00 | 6
1 | 14:45:00 | 2012-08-03 14:45:00 | 3 <-- Most recent entry up until 14:45:00
Run Code Online (Sandbox Code Playgroud)
我们的下一步是采用上面的结果集,并仅为Datetime
每个内容提取最近加入的结果集.我们可以通过GROUP BY
与MAX()
聚合函数结合使用来实现这一点.
不幸的是,由于行为的原因,我们无法正确地拉出Users
每个选定的Datetime
s 的值GROUP BY
.
SELECT a.UID,
b.intrvl,
MAX(c.Datetime) last_date
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
JOIN tbl c ON a.UID = c.UID
AND TIME(b.intrvl) >= TIME(c.Datetime)
GROUP BY a.UID,
b.intrvl
ORDER BY b.intrvl, a.UID DESC --Again, for display purposes
Run Code Online (Sandbox Code Playgroud)
呈现:
UID | intrvl | last_date
---------------------------------------
4 | 14:00:00 | 2012-08-03 14:00:00
3 | 14:00:00 | 2012-08-03 14:00:00
2 | 14:00:00 | 2012-08-03 14:00:00
1 | 14:00:00 | 2012-08-03 14:00:00
4 | 14:15:00 | 2012-08-03 14:00:00
3 | 14:15:00 | 2012-08-03 14:15:00
2 | 14:15:00 | 2012-08-03 14:15:00
1 | 14:15:00 | 2012-08-03 14:15:00
4 | 14:30:00 | 2012-08-03 14:00:00
3 | 14:30:00 | 2012-08-03 14:15:00
2 | 14:30:00 | 2012-08-03 14:15:00
1 | 14:30:00 | 2012-08-03 14:30:00
4 | 14:45:00 | 2012-08-03 14:45:00
3 | 14:45:00 | 2012-08-03 14:45:00
2 | 14:45:00 | 2012-08-03 14:45:00
1 | 14:45:00 | 2012-08-03 14:45:00
Run Code Online (Sandbox Code Playgroud)
现在我们必须抓住Users
每个值,last_date
以便我们可以取这些值的平均值.我们在最后一步为内部子查询包裹我们的查询做这个FROM
条款,对每个匹配的情况再次回来加入到主表UID
> - last_date
协会,抢的价值Users
.
SELECT a.UID,
a.last_date,
b.Users
FROM (
SELECT a.UID,
MAX(c.Datetime) last_date
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
JOIN tbl c ON a.UID = c.UID
AND TIME(b.intrvl) >= TIME(c.Datetime)
GROUP BY a.UID,
b.intrvl
) a
JOIN tbl b ON a.UID = b.UID
AND a.last_date = b.Datetime
ORDER BY a.UID DESC --Display purposes again
Run Code Online (Sandbox Code Playgroud)
呈现:
UID | last_date | Users
---------------------------------
4 | 2012-08-03 14:00:00 | 3
4 | 2012-08-03 14:00:00 | 3
4 | 2012-08-03 14:00:00 | 3
4 | 2012-08-03 14:45:00 | 4
3 | 2012-08-03 14:00:00 | 1
3 | 2012-08-03 14:15:00 | 1
3 | 2012-08-03 14:15:00 | 1
3 | 2012-08-03 14:45:00 | 8
2 | 2012-08-03 14:00:00 | 3
2 | 2012-08-03 14:15:00 | 4
2 | 2012-08-03 14:15:00 | 4
2 | 2012-08-03 14:45:00 | 7
1 | 2012-08-03 14:00:00 | 2
1 | 2012-08-03 14:15:00 | 3
1 | 2012-08-03 14:30:00 | 6
1 | 2012-08-03 14:45:00 | 3
Run Code Online (Sandbox Code Playgroud)
现在,只需在每个房间进行分组并对Users
列进行平均即可:
SELECT b.Name,
AVG(b.Users) avg_users
FROM (
SELECT a.UID,
MAX(c.Datetime) last_date
FROM (SELECT DISTINCT UID FROM tbl) a
CROSS JOIN (
SELECT '14:00:00' intrvl UNION ALL
SELECT '14:15:00' UNION ALL
SELECT '14:30:00' UNION ALL
SELECT '14:45:00'
) b
JOIN tbl c ON a.UID = c.UID
AND TIME(b.intrvl) >= TIME(c.Datetime)
GROUP BY a.UID,
b.intrvl
) a
JOIN tbl b ON a.UID = b.UID
AND a.last_date = b.Datetime
GROUP BY b.UID,
b.Name
Run Code Online (Sandbox Code Playgroud)
呈现:
Name | avg_users
------------------
Room 1 | 3.5
Room 2 | 4.5
Room 3 | 2.75
Room 4 | 3.25
Run Code Online (Sandbox Code Playgroud)