在我的博客上,我想显示上个月的所有帖子。但如果这不到10个帖子,我想显示最近的10个帖子(换句话说,首页上的帖子永远不应少于10个)。我想知道是否有办法在单个查询中执行此操作?
目前,我首先运行此查询:
select count(*) from posts where timestamp > ($thirty_days_ago)
order by timestamp desc
如果该计数大于或等于10:
select * from posts where timestamp > ($thirty_days_ago)
order by timestamp desc
否则:
select * from posts order by timestamp desc limit 10
但是这需要我运行两个查询。使用单个查询有更有效的方法吗? (我正在使用MySQL。)
答案 0 :(得分:5)
(SELECT * FROM posts
WHERE `timestamp` >= NOW() - INTERVAL 30 DAY)
UNION
(SELECT * FROM posts
ORDER BY `timestamp` DESC
LIMIT 10);
编辑:回复@ doofledorfer的评论:我在我的测试数据库上运行了它,它运行正常。我尝试将timestamp
与日期文字以及常量表达式进行比较,如上面的查询所示,但它对优化计划没有任何影响。当然,我使用了大量的数据,如果有数千行,优化计划可能会有所不同。
在任何情况下,OP都在询问如何在单个查询中获得正确的结果,不如何使执行计划达到最佳状态。毕竟这是一个UNION查询,并且必然会产生一个filesort。
+------+--------------+------------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------+------------+------+---------------+------+---------+------+------+----------------+
| 1 | PRIMARY | posts | ALL | timestamp | NULL | NULL | NULL | 20 | Using where |
| 2 | UNION | posts | ALL | NULL | NULL | NULL | NULL | 20 | Using filesort |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+------+--------------+------------+------+---------------+------+---------+------+------+----------------+
答案 1 :(得分:2)
这样做:
select * from posts order by timestamp desc limit 100
并在内存中进一步过滤结果。 (假设100是人们希望在一个页面中看到的“一个月内的帖子”的实际上限)
这是“更有效的单一查询”。
答案 2 :(得分:1)
我只能看到一个查询的唯一方法就是“按时间戳选择帖子”,返回所有帖子,然后处理代码中的显示逻辑。但是,这不是一个非常有效的解决方案。
只要您正确索引表,然后执行选择计数(*)后跟检索查询不应影响性能。是否有任何特殊情况会让您专门尝试避免第二次查询?否则,我认为您的解决方案就足够了。
答案 3 :(得分:1)
不,没有更有效的方法。我会按你在问题中描述的方式来做。 Bill Karwin的答案大致相当于如果修改谓词,就像我上面评论的那样。
到目前为止,我看到的所有其他建议效率都低得多,即使它们以某种方式返回了正确的结果。
答案 4 :(得分:1)
您正在寻找单个表扫描(例如一个SELECT)吗?或者单程往返数据库服务器?比尔的答案只有一次往返,但是有两个SELECT ...所以这是否构成一个或两个“查询”取决于当你说“查询”时你实际上在寻找什么。
如果您对数据库的延迟非常高,比如Bill的解决方案最好,因为您不会非常等待通信。如果加载数据库本身并且表扫描很昂贵,那么原始实现可能会更好,原因有两个:
COUNT
结果,因此每10分钟左右才会执行一次。现在,您已经有效地摊销了该查询的费用(如果在10分钟内有200位访问者访问该页面,那么您只发出了201条SELECT
语句。)COUNT
查询以命中索引而不是完整表,这比尝试将UNION
几个数据集放在一起要快得多。我不确定MySQL是否足够复杂。答案 5 :(得分:0)
我认为你可以尝试类似的东西:
select * from posts
where (timestamp >= (NOW() - INTERVAL 30 DAY)) or
(post_id in (select post_id from posts order by timestamp desc limit 10))
order by timestamp desc
答案 6 :(得分:-1)
Idea1 :执行查询以始终获取本月的帖子。然后做一个循环,计算提取的帖子数量。如果且仅当此数字小于10时,执行第二次查询。
创意2 :为什么不缓存您的第一个查询(Google App Engine,例如,有缓存API)?本月的帖子数量不太可能经常更改,因此在大多数情况下您不需要第一个查询。