在 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()是个好办法