Try to use my solution but use T1 for the table with more rows and T2 with the table which has less rows - and index on the timestamps (or use them as primary keys) so that the > and <= comparisons can go straight to the records instead of having to scan. Also the Window function may be able to optimize a bit more as you always know that you reuse the timestamp rom just one record before. ~~~ SQLite\Examples>sqlite3 search.sqlite SQLite version 3.35.5 2021-04-19 18:32:05 Enter ".help" for usage hints. sqlite> .mode box sqlite> select * from NearestMatch where tsPrev notNull; ┌───┬────────┬─────┬──────┬─────┐ │ a │ tsPrev │ ts │ ts:1 │ b │ ├───┼────────┼─────┼──────┼─────┤ │ B │ 1.0 │ 1.2 │ │ │ │ C │ 1.2 │ 1.8 │ │ │ │ D │ 1.8 │ 2.5 │ 2.0 │ 50 │ │ D │ 1.8 │ 2.5 │ 2.5 │ 100 │ │ E │ 2.5 │ 3.6 │ │ │ │ F │ 3.6 │ 4.5 │ 4.5 │ 200 │ │ G │ 4.5 │ 4.6 │ │ │ │ H │ 4.6 │ 9.8 │ 8.5 │ 300 │ └───┴────────┴─────┴──────┴─────┘ ~~~ created by following example SQL statements: ~~~ drop table if exists T1; create table if not exists T1 ( ts real not null primary key, a char not null ); drop table if exists T2; create table if not exists T2 ( ts real not null primary key, b integer not null ); replace into T1(ts, a) values (1.0, 'A'), (1.2, 'B'), (1.8, 'C'), (2.5, 'D') , (3.6, 'E'), (4.5, 'F'), (4.6, 'G'), (9.8, 'H'); replace into T2(ts, b) values (2.0, 50), (2.5, 100), (4.5, 200), (8.5, 300); drop view if exists NearestMatch; create view NearastMatch as with timeFrames as ( select first_value(T1.ts) over win as tsPrev, * from T1 WINDOW win AS (ORDER BY T1.ts ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) ) select TF.a,TF.tsPrev,TF.ts, T2.* from timeFrames TF left outer join T2 on T2.ts > TF.tsPrev and T2.ts <= TF.ts; select * from NearestMatch; ~~~ Here the windowing function last_value() or first_value() may be faster than lag() or lead() and the range is limited just to the one and only previous record.