我正尝试使用基于以下表结构的MySQL查询来模拟时间轴
drop table if exists testing;
create table testing (
idno integer not null auto_increment,
date datetime not null,
primary key (idno)
);
insert into testing values
(default, '2019-08-25'),
(default, '2019-09-01'),
(default, '2019-09-05'),
(default, '2019-09-10'),
(default, '2019-09-15'),
(default, '2019-09-20');
我要完成的工作是检索从给定日期介于两个日期之间的行开始
例如,我可以执行以下操作
select * from testing
where date >= (
select date from testing
where date <= '2019-09-09 00:00:00'
order by date desc limit 1
)
order by date;
返回所有从2019-09-05起的日期,但如果我使用
select * from testing
where date >= (
select date from testing
where date <= '2019-09-10 00:00:00'
order by date desc limit 1
)
order by date;
然后它将返回2019年9月10日及以后的所有日期,该日期为新的截止日期(这是我想要的)
问题在于,它仅在至少达到一个截止日期时才有效,因此如果我以此查询它
select * from testing
where date >= (
select date from testing
where date <= '2019-08-24 00:00:00'
order by date desc limit 1
)
order by date;
它不返回任何内容,因为子查询试图在date> = none的地方执行操作,我通过更改查询来解决此问题,以解决此问题,但是现在这只是一个大麻烦而已
select * from testing
where (
date >= (
select date from testing
where date <= '2019-08-24 00:00:00'
order by date desc limit 1
) or not exists (
select date from testing
where date <= '2019-08-24 00:00:00'
order by date desc limit 1
)
)
order by date;
是否有一种更清洁的方法来实现这一目标?这样感觉很笨拙
答案 0 :(得分:2)
在这种情况下,> all
表现得很好:
select *
from testing
where date >= all (
select t2.date
from testing t2
where t2.date <= '2019-08-24 00:00:00'
)
order by date;
编辑:
如果没有all
,则可以使用聚合:
select t.*
from testing t
where t.date >= (
select coalesce(max(t2.date), t.date)
from testing t2
where t2.date <= '2019-08-24 00:00:00'
)
order by t.date;
关键是coalesce()
处理NULL
的值(即,所有被过滤掉的行)。
答案 1 :(得分:1)
我认为Gordon的>= ALL
解决方案是最优雅的-但是这里有一些替代方案。
您可以通过添加一行代码来解决您的第一个查询:
select * from testing
where date >= (
select date from testing
where date <= @date
union all select '1000-01-01 00:00:00' -- <--
order by date desc limit 1
)
order by date;
如果没有行与子查询的WHERE条件匹配,则将返回'1000-01-01 00:00:00'
,这是最小的DATETIME值。在这种情况下,所有行都将匹配外部查询的条件。该查询的一个缺点是对UNION集进行排序将导致“文件排序”,而不是仅从索引中收集第一行。但是,由于子查询不相关(不依赖于外部查询的值),因此只需要评估一次。尽管可以通过在第一个UNION部分中添加ORDER BY
和LIMIT 1
来改善这一点:
select * from testing
where date >= (
(
select date from testing
where date <= @date
order by date desc limit 1
)
union all select '1000-01-01 00:00:00'
order by date desc limit 1
)
order by date;
不幸的是,这仅适用于MySQL 8.0。 (可能也可以在MariaDB上工作-但我没有测试。)
您还可以使用当前行的日期值:
select * from testing t1
where date >= (
select date from testing t2
where date <= @date
union all select t1.date
order by date desc limit 1
)
order by date;
但是在这种情况下,将为外部查询的每一行评估子查询。
如果您以“获取日期为<= X的最后一行且日期为> X的所有行”的形式“读取”您的要求,则可以将其“转换”为SQL:
(
select *
from testing
where date <= @date
order by date desc limit 1
) union all (
select *
from testing
where date > @date
)
order by date;
这似乎也可行:
select *
from testing t1
where not exists (
select *
from testing t2
where t2.date > t1.date
and t2.date <= @date
);
但是我无法解释原因。我什至不明白:-)。