2013-08-19 29 views
0

我有一个MySQL表,用于存储来自用户数据的输入并为每个提交的表单添加一个时间戳。限制MySQL行主要按时间排序然后(可能)按行数

表单通常每天都会提交,但用户可以决定每天执行多个表单。

我必须显示一张图表,显示最近6周的数据,或者,如果6周的数据中没有足够的数据点(40),我必须限制点数(它可以从数据点更早的日期)。

我可以在一个查询中做到这一点,或者我应该依赖一些更复杂的SQL或更糟糕的是我必须依靠我的Python/PHP/C++/... wathever?

回顾:

  • 6周最小
  • 数据的如果40个数据点不可用在选定的范围内 - >然后取最后40不管时间戳限制。

澄清

  • 如果在时间范围内的元素的数目为60我希望这些60个元素。
  • 如果时间范围内元素的数量是30,我想要最后的40个元素。
+0

你需要最多40点,或是否有更多的你要吗? – charly

+0

抱歉,我认为这是明显的。如果40(或更多)点在时间范围内可用,然后获得这些点;否则,如果时间范围<40点,则无论时间限制如何都取最后的40点。 – 2dvisio

+0

仍在挣扎吗?考虑提供一个sqlfiddle – Strawberry

回答

1

当然有很多方法只是一个查询来实现这一点,但我怀疑它可以有效地完成。假设你的表上的日期/时间字段建立索引,这样的查询应该是几乎瞬间:

SELECT COUNT(dateField) FROM myTable 
WHERE dateField >= DATE_SUB(NOW(), INTERVAL 6 WEEK); 

然后,我会去两次传球,并触发要么根据的返回值这两个版本的上面的查询:

-- if previous query returned < 40 
SELECT * FROM myTable 
ORDER BY dateField DESC 
LIMIT 40; 

-- if previous query returned >= 40 
SELECT * FROM myTable 
WHERE dateField >= DATE_SUB(NOW(), INTERVAL 6 WEEK); 

通告:

  • NOW()应该由你会计算在过程的开始(否则你就不会在同一个WHERE条件来过滤随着时间的流逝)

  • 第一SELECT语句应该一litteral值替换实际上是一个锁定SELECT ... FOR UPDATE声明,以防止干扰,直到你得到真正的数据(第二个查询)。当然,这会在一次交易中发生。

+0

我应该如何将第二个与第一个连接? ()选择时间戳>(选择时间从FROM MyForms WHERE timestp> UNIX_TIMESTAMP() - (30)(30 * 24 * 3600))> 40, 我试过 'SELECT IF((SELECT COUNT(timestsp)FROM MyForms WHERE timestsp> UNIX_TIMESTAMP * 24 * 3600)), (SELECT timestp FROM MyForms LIMIT 30))' 但它没有工作。 – 2dvisio

+1

@ 2dvisio你将不会链接它们。你会执行两个查询。首先你会执行count()查询。根据您的发现,您可以执行两个select *查询中的一个。或者,你可以建立一个存储例程的两个过程,只需调用存储例程一次 – zedfoxus

+0

我接受了这个答案,因为它似乎是唯一合理的。 尽管我不喜欢使用多个查询的方法,但它似乎仍然是可缩放的,而其他人可能会遇到后续的性能问题。 – 2dvisio

1
select t2.* from (
SELECT t.*, 
    @rownum := @rownum + 1 AS rownumber 
FROM YOUR_TABLE t, 
    (SELECT @rownum := 0) r 
ORDER BY timestamp 
) as t2 
where t2.rownumber<=40 or t2.timestamp >= '6-week-date' 
+0

这似乎没有做我在问什么... 它似乎选择了第一个40分,另外还有那些时间戳大于6周的日期。 – 2dvisio

+1

'ORDER BY'子句缺失,所以我似乎(http://www.sqlfiddle.com/#!2/f64f5/3)。 – RandomSeed

+0

我认为这个查询可能会随着表的增长而变得非常缓慢,不是吗?我有相当多的积分... – 2dvisio

0

我会做这样的:

SELECT t.* 
    FROM mytable t 
CROSS 
    JOIN (SELECT COUNT(1) AS cnt 
      FROM mytable ct 
      WHERE c.submitted_timestamp >= NOW() + INTERVAL -6 WEEK 
     ) c 
WHERE (c.cnt <= 40) 
    OR (c.cnt > 40 AND t.submitted_timestamp >= NOW() + INTERVAL -6 WEEK) 
ORDER 
    BY t.submitted_timestamp DESC 
LIMIT 40 

内联视图(分配的上述c别名)返回与指定的时间周期内的时间戳的数目的计数(6周)。

我们使用外部查询的WHERE子句中返回的计数值。如果计数小于40,那么我们不包含任何其他谓词(获取所有行)。

如果计数大于40,那么我们包含一个额外的谓词。

ORDER BY向我们保证我们有最先的行。 LIMIT确保我们返回不超过40行。

(用submitted_timestamp领先列的索引可能会提高性能。

+0

如果时间范围内的元素数量是60,我想要60个元素。 如果时间范围内的元素数量是30,我想要最后的40个元素。 – 2dvisio

相关问题