在 SQL table 上搜索关闭索引
Search for closes index on SQL table
我有一个假设的 SQL table “EVENTS”,有两列,一个 UUID 索引列和一个 DateTime 列,
table 填充了从 1900-01-01
到今天的值,它没有排序,缺少许多日期。
我必须 运行 的查询基本上是 'retrieve all events that happened at the requested date (start to the end of the day) or the closest previous date'
如果我要查找一天中我知道存在于数据库中的所有事件,它会像这样简单:
SELECT * FROM Events e
WHERE
e.date BETWEEN $START_OF_DAY AND $END_OF_DAY;
但如果该日期不存在,我必须检索到请求日期的最新日期。
抓取当天,但如果没有找到记录,将 return 所有最近一天的记录和记录。
所以在我的样本数据中,1 月 2 日 returns 3 个事件日期为 1 月 1 日
SQL 服务器解决方案
DECLARE @Input DATE = '2022-01-02' /*Try Jan 1,2,3, or 4*/
DROP TABLE IF EXISTS #Event
CREATE TABLE #Event (ID INT IDENTITY(1,1),EventDateTime DATETIME)
INSERT INTO #Event
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00')
SELECT TOP (1) WITH TIES *
FROM #Event AS A
CROSS APPLY (SELECT EventDate = CAST(EventDateTime AS DATE)) AS B
WHERE B.EventDate <= @Input
ORDER BY B.EventDate DESC
SQL Fiddle 不允许我创建变量,但这里是概念上的代码,用于 MySQL 的更高效版本。它在第一个查询中获取所需的日期范围,然后使用它在第二个查询中进行过滤。假设您在 EventDateTime
上有索引,我认为它的表现应该比公认的答案好得多
CREATE TABLE Event (
ID MEDIUMINT NOT NULL AUTO_INCREMENT
,EventDateTime DATETIME
,PRIMARY KEY (ID));
INSERT INTO Event (EventDateTime)
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00');
/*Need to save these off to variables to use in later query*/
SELECT TIMESTAMP(CAST(EventDateTime AS DATE)) AS StartRange
,TIMESTAMP(CAST(EventDateTime AS DATE)) + INTERVAL 1 DAY AS EndRange
FROM Event
WHERE EventDateTime < DATE_ADD('2022-01-04' /*Input*/,INTERVAL 1 DAY)
ORDER BY EventDateTime DESC
LIMIT 1;
SELECT *
FROM Event
WHERE EventDateTime >= StartRange
AND EventDateTime < EndRange
计算最近的日期,并进行自连接。虽然我使用的是 MYSQL,但我相信这是最通用的解决方法
CREATE TABLE d0207Event (ID INT ,EventDateTime DATETIME)
INSERT INTO d0207Event
VALUES
(1,'2022-01-01 08:00')
,(2,'2022-01-01 09:00')
,(3,'2022-01-01 10:00')
,(4,'2022-01-03 12:00')
INSERT INTO d0207Event
VALUES
(5, '2021-12-12 08:00');
select t1.*
from d0207Event t1,
(
select min(t1.dat) mindat
from (
select t1.*,
DATEDIFF('2022-01-02', cast(t1.EventDateTime as date)) dat
from d0207Event t1
) t1
where t1.dat >= 0
) t2
where DATEDIFF('2022-01-02', cast(t1.EventDateTime as date)) = t2.mindat
;
还有很多高级语法可以更好的解决这个问题,看你用的是什么DB,具体的应用场景
貌似也可以选择语法比较多的数据库,那么使用解析函数通常可以很好的解决效率问题,因为EVENTtable只需要查询一次
CREATE TABLE Event (
ID MEDIUMINT NOT NULL AUTO_INCREMENT
,EventDateTime DATETIME
,PRIMARY KEY (ID));
INSERT INTO Event (EventDateTime)
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00');
select *
from (
select t1.*,
first_value(cast(t1.EventDateTime as date))
over(order by cast(t1.EventDateTime as date) desc) fv
from event t1
where cast(t1.EventDateTime as date) <= '2022-01-03'
) t1
where cast(t1.EventDateTime as date) = fv
创建函数式索引cast(t1.EventDateTime as date)
,或者直接创建虚拟列可以使查询更简单,否则使用date_add()是个好办法
我有一个假设的 SQL table “EVENTS”,有两列,一个 UUID 索引列和一个 DateTime 列,
table 填充了从 1900-01-01
到今天的值,它没有排序,缺少许多日期。
我必须 运行 的查询基本上是 'retrieve all events that happened at the requested date (start to the end of the day) or the closest previous date'
如果我要查找一天中我知道存在于数据库中的所有事件,它会像这样简单:
SELECT * FROM Events e
WHERE
e.date BETWEEN $START_OF_DAY AND $END_OF_DAY;
但如果该日期不存在,我必须检索到请求日期的最新日期。
抓取当天,但如果没有找到记录,将 return 所有最近一天的记录和记录。 所以在我的样本数据中,1 月 2 日 returns 3 个事件日期为 1 月 1 日
SQL 服务器解决方案
DECLARE @Input DATE = '2022-01-02' /*Try Jan 1,2,3, or 4*/
DROP TABLE IF EXISTS #Event
CREATE TABLE #Event (ID INT IDENTITY(1,1),EventDateTime DATETIME)
INSERT INTO #Event
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00')
SELECT TOP (1) WITH TIES *
FROM #Event AS A
CROSS APPLY (SELECT EventDate = CAST(EventDateTime AS DATE)) AS B
WHERE B.EventDate <= @Input
ORDER BY B.EventDate DESC
SQL Fiddle 不允许我创建变量,但这里是概念上的代码,用于 MySQL 的更高效版本。它在第一个查询中获取所需的日期范围,然后使用它在第二个查询中进行过滤。假设您在 EventDateTime
上有索引,我认为它的表现应该比公认的答案好得多CREATE TABLE Event (
ID MEDIUMINT NOT NULL AUTO_INCREMENT
,EventDateTime DATETIME
,PRIMARY KEY (ID));
INSERT INTO Event (EventDateTime)
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00');
/*Need to save these off to variables to use in later query*/
SELECT TIMESTAMP(CAST(EventDateTime AS DATE)) AS StartRange
,TIMESTAMP(CAST(EventDateTime AS DATE)) + INTERVAL 1 DAY AS EndRange
FROM Event
WHERE EventDateTime < DATE_ADD('2022-01-04' /*Input*/,INTERVAL 1 DAY)
ORDER BY EventDateTime DESC
LIMIT 1;
SELECT *
FROM Event
WHERE EventDateTime >= StartRange
AND EventDateTime < EndRange
计算最近的日期,并进行自连接。虽然我使用的是 MYSQL,但我相信这是最通用的解决方法
CREATE TABLE d0207Event (ID INT ,EventDateTime DATETIME)
INSERT INTO d0207Event
VALUES
(1,'2022-01-01 08:00')
,(2,'2022-01-01 09:00')
,(3,'2022-01-01 10:00')
,(4,'2022-01-03 12:00')
INSERT INTO d0207Event
VALUES
(5, '2021-12-12 08:00');
select t1.*
from d0207Event t1,
(
select min(t1.dat) mindat
from (
select t1.*,
DATEDIFF('2022-01-02', cast(t1.EventDateTime as date)) dat
from d0207Event t1
) t1
where t1.dat >= 0
) t2
where DATEDIFF('2022-01-02', cast(t1.EventDateTime as date)) = t2.mindat
;
还有很多高级语法可以更好的解决这个问题,看你用的是什么DB,具体的应用场景
貌似也可以选择语法比较多的数据库,那么使用解析函数通常可以很好的解决效率问题,因为EVENTtable只需要查询一次
CREATE TABLE Event (
ID MEDIUMINT NOT NULL AUTO_INCREMENT
,EventDateTime DATETIME
,PRIMARY KEY (ID));
INSERT INTO Event (EventDateTime)
VALUES
('2022-01-01 08:00')
,('2022-01-01 09:00')
,('2022-01-01 10:00')
,('2022-01-03 12:00');
select *
from (
select t1.*,
first_value(cast(t1.EventDateTime as date))
over(order by cast(t1.EventDateTime as date) desc) fv
from event t1
where cast(t1.EventDateTime as date) <= '2022-01-03'
) t1
where cast(t1.EventDateTime as date) = fv
创建函数式索引cast(t1.EventDateTime as date)
,或者直接创建虚拟列可以使查询更简单,否则使用date_add()是个好办法