【问题标题】:Limit MySQL row primarily by time and then (maybe) by number of rows主要按时间限制 MySQL 行,然后(可能)按行数限制
【发布时间】:2013-08-19 17:06:41
【问题描述】:

我有一个 MySQL 表,它存储来自用户数据的输入,并为每个提交的表单添加时间戳。

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

我必须显示一个图表来显示过去 6 周的数据,或者,如果 6 周的数据不包含足够的数据点 (40),我必须限制点的数量(它可以从之前获取数据点日期)。

我可以在一个查询中执行此操作,还是应该依赖一些更复杂的 SQL,或者更糟糕的是,我必须依赖我的 Python/PHP/C++/... 不管怎样?

回顾:

  • 最少 6 周的数据
  • 如果所选范围内没有 40 个数据点 -> 无论时间戳限制如何,都取最后 40 个数据点。

澄清

  • 如果时间范围内的元素数为 60,我想要这 60 个元素。
  • 如果时间范围内的元素数为 30,我想要最后 40 个元素。

【问题讨论】:

  • 您最多需要 40 个点还是需要更多点?
  • 对不起。我以为很清楚。如果时间范围内有 40 个(或更多)点可用,则获取这些点。否则,如果时间范围有
  • 还在苦苦挣扎?考虑提供一个 sqlfiddle

标签: mysql


【解决方案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语句实际上应该是一个锁定SELECT ... FOR UPDATE语句,以防止干扰直到你得到真实的数据(第二个查询)。当然,这会发生在一笔交易中。

【讨论】:

  • 我应该如何链接第二个和第一个?我试过SELECT IF( (SELECT COUNT(timestsp) FROM MyForms WHERE timestsp &gt; UNIX_TIMESTAMP() - (30 * 24 * 3600)) &gt; 40, (SELECT timestp FROM MyForms WHERE timestp &gt; UNIX_TIMESTAMP() - (30 * 24 * 3600)), (SELECT timestp FROM MyForms LIMIT 30)),但没用。
  • @2dvisio 您不会链接它们。您将执行两个查询。首先,您将执行 count() 查询。根据您的发现,您将执行两个 select * 查询之一。或者,您可以为这两个通道构建一个存储例程,然后只调用一次存储例程
  • 我接受了这个答案,因为它似乎是唯一合理的答案。虽然我不喜欢使用多个查询的方法,但它似乎仍然是可扩展的,而其他查询可能会在以后遇到性能问题。
【解决方案2】:
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'

【讨论】:

  • 这似乎不符合我的要求...似乎它选择了前 40 个点以及时间戳大于 6 周日期的点。
  • 缺少一个 ORDER BY 子句,所以看起来是 (sqlfiddle.com/#!2/f64f5/3)。
  • 我认为随着表的增长,这个查询可能会变得非常慢,不是吗?我的积分还蛮多的……
【解决方案3】:

我会这样做:

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 的索引可能会提高性能。

【讨论】:

  • 如果时间范围内的元素数量为 60,我想要 60 个元素。如果时间范围内的元素数是 30,我想要最后 40 个元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多