我有一个如下所示的查询:
SELECT time_start, some_count
FROM foo
WHERE user_id = 1
AND DATE(time_start) = '2016-07-27'
ORDER BY some_count DESC, time_start DESC LIMIT 1;
这样做会让我返回一行,其中some_count是user_id = 1
的最高计数。它还为我提供了some_count
的最新时间戳,因为some_count
对于多个time_start
值可能相同,我想要最新的user_id
。
现在我要做的是运行一个查询,该查询会针对特定日期至少发生过一次的每个2016-07-27
计算出来,在本例中为user_id
。最终它可能需要GROUP BY,因为我正在寻找每outline: 3px solid #725b44;
组的最大值
编写这种性质的查询的最佳方法是什么?
答案 0 :(得分:2)
我正在分享我的两种方法。
方法#1(可扩展):
使用MySQL user_defined variables
SELECT
t.user_id,
t.time_start,
t.time_stop,
t.some_count
FROM
(
SELECT
user_id,
time_start,
time_stop,
some_count,
IF(@sameUser = user_id, @rn := @rn + 1,
IF(@sameUser := user_id, @rn := 1, @rn := 1)
) AS row_number
FROM foo
CROSS JOIN (
SELECT
@sameUser := - 1,
@rn := 1
) var
WHERE DATE(time_start) = '2016-07-27'
ORDER BY user_id, some_count DESC, time_stop DESC
) AS t
WHERE t.row_number <= 1
ORDER BY t.user_id;
可扩展,因为如果您想为每个用户提供最新的n行,那么您只需更改此行:
... WHERE t.row_number <= n...
如果查询提供了预期的结果
,我可以稍后添加解释方法#2 :(不可扩展)
使用INNER JOIN and GROUP BY
SELECT
F.user_id,
F.some_count,
F.time_start,
MAX(F.time_stop) AS max_time_stop
FROM foo F
INNER JOIN
(
SELECT
user_id,
MAX(some_count) AS max_some_count
FROM foo
WHERE DATE(time_start) = '2016-07-27'
GROUP BY user_id
) AS t
ON F.user_id = t.user_id AND F.some_count = t.max_some_count
WHERE DATE(time_start) = '2016-07-27'
GROUP BY F.user_id
答案 1 :(得分:1)
您可以使用BottomUp
:
btn.AutoSizeMode = AutoSizeMode.GrowOnly;
btn.AutoSize = true;
btn.Dock = DockStyle.Fill;
NOT EXISTS()
将仅选择记录为另一个记录较大的记录或另一记录具有相同的计数但较新的SELECT * FROM foo t
WHERE (DATE(time_start) = '2016-07-27'
OR DATE(time_stop) = '2016-07-27')
AND NOT EXISTS(SELECT 1 FROM foo s
WHERE t.user_id = s.user_id
AND (s.some_count > t.some_count
OR (s.some_count = t.some_count
AND s.time_stop > t.time_stop)))
不存在的记录。
答案 2 :(得分:1)
您可以将原始查询用作WHERE子句中的相关子查询。
SELECT user_id, time_stop, some_count
FROM foo f
WHERE f.id = (
SELECT f1.id
FROM foo f1
WHERE f1.user_id = f.user_id -- correlate
AND DATE(f1.time_start) = '2016-07-27'
ORDER BY f1.some_count DESC, f1.time_stop DESC LIMIT 1
)
MySQL应该能够为每个不同的user_id
缓存子查询的结果。
另一种方法是使用嵌套的GROUP BY查询:
select f.user_id, f.some_count, max(f.time_stop) as time_stop
from (
select f.user_id, max(f.some_count) as some_count
from foo f
where date(f.time_start) = '2016-07-27'
group by f.user_id
) sub
join foo f using(user_id, some_count)
where date(f.time_start) = '2016-07-27'
group by f.user_id, f.some_count
答案 3 :(得分:1)
SELECT user_id,
some_count,
max(time_start) AS time_start
FROM
(SELECT a.*
FROM foo AS a
INNER JOIN
(SELECT user_id,
max(some_count) AS some_count
FROM foo
WHERE DATE(time_start) = '2016-07-27'
GROUP BY user_id) AS b ON a.user_id = b.user_id
AND a.some_count = b.some_count) AS c
GROUP BY user_id,
some_count;
从内到外解释:最内层的表(b)将为每位用户提供最大的some_count。这还不够,因为你想要两列的最大值 - 所以我将它与完整的表(a)连接起来以获得具有这些最大值(c)的记录,并从我那里获取每个用户/ some_count组合的最大time_start。
答案 4 :(得分:1)
<强>策略强>
通常,找到最大值而不是对记录组进行排序会更有效。在这种情况下,排序是一个整数(some_count
),后跟一个日期/时间(time_start
) - 所以要找到一个最大行,我们需要以某种方式组合这些。
这样做的一个简单方法是将两者组合成一个字符串,但通常会将"4"
的字符串比较值高于"12"
。使用LPAD
添加前导零可以轻松克服这一问题,因此4
变为"0000000004"
,在字符串比较中低于"0000000012"
。假设time_start
是DATETIME
字段,可以简单地将其附加到此字段以进行辅助排序,因为其字符串转换会产生可排序的格式(yyyy-mm-dd hh:MM:ss
)。
<强> SQL 强>
使用此策略,我们可以通过简单的子选择来限制:
SELECT time_start, some_count
FROM foo f1
WHERE DATE(time_start) = '2016-07-27'
AND CONCAT(LPAD(some_count, 10, '0'), time_start) =
(SELECT MAX(CONCAT(LPAD(some_count, 10, '0'), time_start))
FROM foo f2
WHERE DATE(f2.time_start) = '2016-07-27'
AND f2.user_id = f1.user_id);
<强>演示强>
Rextester演示:http://rextester.com/HCGY1362
答案 5 :(得分:0)
我相信,你不需要为查询做任何花哨的事情。 只需按 user_id 按升序对表格进行排序,然后按 some_count 和 time_start 按降序排序,并从有序表格中选择预期字段GROUP BY USER_ID 即可。这很简单。如果有效,请尝试告诉我。
SELECT user_id, some_count, time_start
FROM (SELECT * FROM foo ORDER BY user_id ASC, some_count DESC, time_start DESC)sorted_foo
WHERE DATE( time_start ) = '2016-07-27'
GROUP BY user_id
答案 6 :(得分:0)
您的问题可以通过窗口函数解决,但MySQL不支持此类功能。
我有两个解决方案。一个是模拟窗口函数,另一个是你在MySQL中编写一些查询来解决这些情况的常用方法。
这是第一个,我回答this question:
-- simulates the window function
-- first_value(<col>) over(partition by user_id order by some_count DESC, time_start DESC)
SELECT
user_id,
substring_index(group_concat(time_start ORDER BY some_count DESC, time_start DESC), ',', 1) time_start,
substring_index(group_concat(some_count ORDER BY some_count DESC, time_start DESC), ',', 1) some_count
FROM foo
WHERE DATE(time_start) = '2016-07-27'
GROUP BY user_id
;
基本上,您按user_id
对数据进行分组,并使用,
分隔符连接指定列中的所有值,按所需的列排序,每个组,然后只对第一个有序的子字符串进行子串值。这不是一种最佳方法......
那是第二个,我回答this question:
SELECT
user_id,
some_count,
MAX(time_start) time_start
FROM foo outq
WHERE 1=1
AND DATE(time_start) = '2016-07-27'
AND NOT EXISTS
(
SELECT 1
FROM foo
WHERE 1=1
AND user_id = outq.user_id
AND some_count > outq.some_count
AND DATE(time_start) = DATE(outq.time_start)
)
GROUP BY
user_id,
some_count
;
基本上,子查询检查每个user_id
,如果在该日期检查了当前的some_count
个NOT EXISTS
,则主要查询期望它some_count
。您在某个日期中,每个user_id
的{{1}}最高time_start
,但是对于该用户的相同最高值,该日期可能会存在多个不同的GROUP BY
。现在事情很简单。您可以安全time_start
用户和计数,因为它们已经是您想要的数据,并从组中获取最大ORDER BY <columns>
。
这种子查询是解决像MySQL这样的问题的常用方法。我建议你尝试两种解决方案,但选择第二种解决方案并记住子查询sintax以解决任何未来的问题。
此外,在MySQL中,隐式GROUP BY <columns>
适用于所有ORDER BY NULL
的查询。如果您不打扰结果订单,可以通过声明{{1}}来保存一些处理,这将禁用查询中的隐式ordenation功能。
答案 7 :(得分:0)
SELECT c1.user_id, c1.some_count, MAX(c1.time_start) AS time_start
FROM foo AS c1
JOIN
( SELECT user_id, MAX(some_count) AS some_count
FROM foo
WHERE time_start >= '2016-07-27'
AND time_start < '2016-07-27' + INTERVAL 1 DAY
GROUP BY user_id
) AS c2 USING (user_id, some_count)
GROUP BY c1.user_id, c1.some_count
并添加这些以获得更好的性能:
INDEX(user_id, some_count, time_start)
INDEX(time_start)
更改了time_start
范围的测试,以便可以使用第二个索引。
这很大程度上来源于groupwise max上的博客。