sentencia obtener longitud length len funciĆ³n ejemplos contar caracteres campo cadena sql oracle select oracle12c asset-management

sql - obtener - Agregue segmentos superpuestos para medir la longitud efectiva



sentencia length sql (6)

Este hallazgo expande la tabla para producir una fila por cada milla de cada camino, y simplemente toma el año MAX . En ese momento podemos COUNT el número de filas para producir el evento_length.

Produce la tabla exactamente como se especificó anteriormente.

Nota: Ejecuté esta consulta contra SQL Server. Podría usar LEAST lugar de SELECT MIN(event_length) FROM (VALUES...) en Oracle, creo.

WITH NumberRange(result) AS ( SELECT 0 UNION ALL SELECT result + 1 FROM NumberRange WHERE result < 301 --Max length of any road ), CurrentRoadEventLength(road_id, [year], event_length) AS ( SELECT road_id, [year], COUNT(*) AS event_length FROM ( SELECT re.road_id, n.result, MAX(re.[year]) as [year] FROM road_events re INNER JOIN NumberRange n ON ( re.from_meas <= n.result AND re.to_meas > n.result ) GROUP BY re.road_id, n.result ) events_per_mile GROUP BY road_id, [year] ) SELECT re.event_id, re.road_id, re.[year], re.total_road_length, (SELECT MIN(event_length) FROM (VALUES (re.to_meas - re.from_meas), (cre.event_length)) AS EventLengths(event_length)) FROM road_events re INNER JOIN CurrentRoadEventLength cre ON ( re.road_id = cre.road_id AND re.[year] = cre.[year] ) ORDER BY re.event_id, re.road_id OPTION (MAXRECURSION 301) --Max length of any road

Tengo una mesa de road_events :

create table road_events ( event_id number(4,0), road_id number(4,0), year number(4,0), from_meas number(10,2), to_meas number(10,2), total_road_length number(10,2) ); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (1,1,2020,25,50,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (2,1,2000,25,50,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (3,1,1980,0,25,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (4,1,1960,75,100,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (5,1,1940,1,100,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (6,2,2000,10,30,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (7,2,1975,30,60,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (8,2,1950,50,90,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (9,3,2050,40,90,100); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (10,4,2040,0,200,200); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (11,4,2013,0,199,200); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (12,4,2001,0,200,200); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (13,5,1985,50,70,300); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (14,5,1985,10,50,300); insert into road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) values (15,5,1965,1,301,300); commit; select * from road_events;

EVENT_ID ROAD_ID YEAR FROM_MEAS TO_MEAS TOTAL_ROAD_LENGTH ---------- ---------- ---------- ---------- ---------- ----------------- 1 1 2020 25 50 100 2 1 2000 25 50 100 3 1 1980 0 25 100 4 1 1960 75 100 100 5 1 1940 1 100 100 6 2 2000 10 30 100 7 2 1975 30 60 100 8 2 1950 50 90 100 9 3 2050 40 90 100 10 4 2040 0 200 200 11 4 2013 0 199 200 12 4 2001 0 200 200 13 5 1985 50 70 300 14 5 1985 10 50 300 15 5 1965 1 301 300

Quiero seleccionar los eventos que representan el trabajo más reciente en cada camino.

Esta es una operación difícil, porque los eventos a menudo pertenecen a solo una parte de la carretera . Esto significa que no puedo simplemente seleccionar el evento más reciente por carretera; Solo necesito seleccionar el kilometraje más reciente del evento que no se superponga.

Posible lógica (en orden):

Me resisto a adivinar cómo podría resolverse este problema, ya que podría terminar dañando más de lo que ayuda (algo así como el problema XY ). Por otro lado, podría proporcionar una visión de la naturaleza del problema, por lo que aquí va:

  1. Seleccione el evento más reciente para cada camino. Llamaremos al evento más reciente: event A
  2. Si el event A es >= total_road_length , eso es todo lo que necesito. El algoritmo termina aquí.
  3. De lo contrario, obtenga el siguiente evento cronológico ( event B ) que no tenga las mismas extensiones que el event A
  4. Si las extensiones del event B superponen a las extensiones del event A , entonces solo obtenga la (s) porción (es) del event B que no se superponen.
  5. Repita los pasos 3 y 4 hasta que la duración total del evento sea = total_road_length . O deténgase cuando no haya más eventos para ese camino.

Pregunta:

Sé que es una tarea difícil, pero ¿qué se necesitaría para hacer esto?

Este es un problema de referencia lineal clásico. Sería extremadamente útil si pudiera hacer operaciones de referencia lineales como parte de las consultas.

El resultado sería:

EVENT_ID ROAD_ID YEAR TOTAL_ROAD_LENGTH EVENT_LENGTH ---------- ---------- ---------- ----------------- ------------ 1 1 2020 100 25 3 1 1980 100 25 4 1 1960 100 25 5 1 1940 100 25 6 2 2000 100 20 7 2 1975 100 30 8 2 1950 100 30 9 3 2050 100 50 10 4 2040 200 200 13 5 1985 300 20 14 5 1985 300 40 15 5 1965 300 240

Pregunta relacionada: seleccione donde el rango de números no se superponga


Hay otra forma de calcular esto, con valores desde y hasta:

with part_begin_point as ( Select distinct road_id, from_meas point from road_events be union Select distinct road_id, to_meas point from road_events ee ) , newest_part as ( select e.event_id , e.road_id , e.year , e.total_road_length , p.point , LAG(e.event_id) over (partition by p.road_id order by p.point) prev_event , e.to_meas event_to_meas from part_begin_point p join road_events e on p.road_id = e.road_id and p.point >= e.from_meas and p.point < e.to_meas and not exists( select 1 from road_events ne where e.road_id = ne.road_id and p.point >= ne.from_meas and p.point < ne.to_meas and (e.year < ne.year or e.year = ne.year and e.event_id < ne.event_id)) ) select event_id, road_id, year , point from_meas , LEAD(point, 1, event_to_meas) over (partition by road_id order by point) to_meas , total_road_length , LEAD(point, 1, event_to_meas) over (partition by road_id order by point) - point EVENT_LENGTH from newest_part where 1=1 and event_id <> prev_event or prev_event is null order by event_id, point

SQL Fiddle


Mi DBMS principal es Teradata, pero esto también funcionará como está en Oracle.

WITH all_meas AS ( -- get a distinct list of all from/to points SELECT road_id, from_meas AS meas FROM road_events UNION SELECT road_id, to_meas FROM road_events ) -- select * from all_meas order by 1,2 , all_ranges AS ( -- create from/to ranges SELECT road_id, meas AS from_meas ,Lead(meas) Over (PARTITION BY road_id ORDER BY meas) AS to_meas FROM all_meas ) -- SELECT * from all_ranges order by 1,2 , all_event_ranges AS ( -- now match the ranges to the event ranges SELECT ar.* ,re.event_id ,re.year ,re.total_road_length ,ar.to_meas - ar.from_meas AS event_length -- used to filter the latest event as multiple events might cover the same range ,Row_Number() Over (PARTITION BY ar.road_id, ar.from_meas ORDER BY year DESC) AS rn FROM all_ranges ar JOIN road_events re ON ar.road_id = re.road_id AND ar.from_meas < re.to_meas AND ar.to_meas > re.from_meas WHERE ar.to_meas IS NOT NULL ) SELECT event_id, road_id, year, total_road_length, Sum(event_length) FROM all_event_ranges WHERE rn = 1 -- latest year only GROUP BY event_id, road_id, year, total_road_length ORDER BY road_id, year DESC;

Si necesita devolver la cobertura real from/to_meas (como en su pregunta antes de editar), podría ser más complicado. La primera parte es la misma, pero sin agregación, la consulta puede devolver filas adyacentes con el mismo evento_id (por ejemplo, para el evento 3: 0-1 y 1-25):

SELECT * FROM all_event_ranges WHERE rn = 1 ORDER BY road_id, from_meas;

Si desea combinar filas adyacentes, necesita dos pasos más (utilizando un enfoque estándar, marque la primera fila de un grupo y calcule un número de grupo):

WITH all_meas AS ( SELECT road_id, from_meas AS meas FROM road_events UNION SELECT road_id, to_meas FROM road_events ) -- select * from all_meas order by 1,2 , all_ranges AS ( SELECT road_id, meas AS from_meas ,Lead(meas) Over (PARTITION BY road_id ORDER BY meas) AS to_meas FROM all_meas ) -- SELECT * from all_ranges order by 1,2 , all_event_ranges AS ( SELECT ar.* ,re.event_id ,re.year ,re.total_road_length ,ar.to_meas - ar.from_meas AS event_length ,Row_Number() Over (PARTITION BY ar.road_id, ar.from_meas ORDER BY year DESC) AS rn FROM all_ranges ar JOIN road_events re ON ar.road_id = re.road_id AND ar.from_meas < re.to_meas AND ar.to_meas > re.from_meas WHERE ar.to_meas IS NOT NULL ) -- SELECT * FROM all_event_ranges WHERE rn = 1 ORDER BY road_id, from_meas , adjacent_events AS ( -- assign 1 to the 1st row of an event SELECT t.* ,CASE WHEN Lag(event_id) Over(PARTITION BY road_id ORDER BY from_meas) = event_id THEN 0 ELSE 1 END AS flag FROM all_event_ranges t WHERE rn = 1 ) -- SELECT * FROM adjacent_events ORDER BY road_id, from_meas , grouped_events AS ( -- assign a groupnumber to adjacent rows using a Cumulative Sum over 0/1 SELECT t.* ,Sum(flag) Over (PARTITION BY road_id ORDER BY from_meas ROWS Unbounded Preceding) AS grp FROM adjacent_events t ) -- SELECT * FROM grouped_events ORDER BY road_id, from_meas SELECT event_id, road_id, year, Min(from_meas), Max(to_meas), total_road_length, Sum(event_length) FROM grouped_events GROUP BY event_id, road_id, grp, year, total_road_length ORDER BY 2, Min(from_meas);

Editar:

Ups, acabo de encontrar un blog. Rangos superpuestos con prioridad haciendo exactamente lo mismo con alguna sintaxis de Oracle simplificada. De hecho, traduje mi consulta de otra sintaxis simplificada en Teradata a Estándar / Oracle SQL :-)


Pensé demasiado en esto hoy, pero tengo algo que ignora los +/- 10 metros ahora.

Primero hizo una función que toma desde / hacia pares como una cadena y devuelve la distancia cubierta por los pares en la cadena. Por ejemplo, ''10: 20; 35: 45 ''devuelve 20.

CREATE OR replace FUNCTION get_distance_range_str (strRangeStr VARCHAR2) RETURN NUMBER IS intRetNum NUMBER; BEGIN --split input string WITH cte_1 AS ( SELECT regexp_substr(strRangeStr, ''[^;]+'', 1, LEVEL) AS TO_FROM_STRING FROM dual connect BY regexp_substr(strRangeStr, ''[^;]+'', 1, LEVEL) IS NOT NULL ) --split From/To pairs ,cte_2 AS ( SELECT cte_1.TO_FROM_STRING ,to_number(substr(cte_1.TO_FROM_STRING, 1, instr(cte_1.TO_FROM_STRING, '':'') - 1)) AS FROM_MEAS ,to_number(substr(cte_1.TO_FROM_STRING, instr(cte_1.TO_FROM_STRING, '':'') + 1, length(cte_1.TO_FROM_STRING) - instr(cte_1.TO_FROM_STRING, '':''))) AS TO_MEAS FROM cte_1 ) --merge ranges ,cte_merge_ranges AS ( SELECT s1.FROM_MEAS , --t1.TO_MEAS MIN(t1.TO_MEAS) AS TO_MEAS FROM cte_2 s1 INNER JOIN cte_2 t1 ON s1.FROM_MEAS <= t1.TO_MEAS AND NOT EXISTS ( SELECT * FROM cte_2 t2 WHERE t1.TO_MEAS >= t2.FROM_MEAS AND t1.TO_MEAS < t2.TO_MEAS ) WHERE NOT EXISTS ( SELECT * FROM cte_2 s2 WHERE s1.FROM_MEAS > s2.FROM_MEAS AND s1.FROM_MEAS <= s2.TO_MEAS ) GROUP BY s1.FROM_MEAS ) SELECT sum(TO_MEAS - FROM_MEAS) AS DISTANCE_COVERED INTO intRetNum FROM cte_merge_ranges; RETURN intRetNum; END;

Luego escribió esta consulta que crea una cadena para esa función para el rango anterior apropiado. No se pudo usar el uso de ventanas con list_agg, pero fue capaz de lograr lo mismo con una subconsulta correlacionada.

--use list agg to create list of to/from pairs for rows before current row in the ordering WITH cte_2 AS ( SELECT T1.* ,( SELECT LISTAGG(FROM_MEAS || '':'' || TO_MEAS || '';'') WITHIN GROUP ( ORDER BY ORDER BY YEAR DESC, EVENT_ID DESC ) FROM road_events T2 WHERE T1.YEAR || lpad(T1.EVENT_ID, 10,''0'') < T2.YEAR || lpad(T2.EVENT_ID, 10,''0'') AND T1.ROAD_ID = T2.ROAD_ID GROUP BY road_id ) AS PRIOR_RANGES_STR FROM road_events T1 ) --get distance for prior range string - distance ignoring current row --get distance including current row ,cte_3 AS ( SELECT cte_2.* ,coalesce(get_distance_range_str(PRIOR_RANGES_STR), 0) AS DIST_PRIOR ,get_distance_range_str(PRIOR_RANGES_STR || FROM_MEAS || '':'' || TO_MEAS || '';'') AS DIST_NOW FROM cte_2 cte_2 ) --distance including current row less distance ignoring current row is distance added to the range this row ,cte_4 AS ( SELECT cte_3.* ,DIST_NOW - DIST_PRIOR AS DIST_ADDED_THIS_ROW FROM cte_3 ) SELECT * FROM cte_4 --filter out any rows with distance added as 0 WHERE DIST_ADDED_THIS_ROW > 0 ORDER BY ROAD_ID, YEAR DESC, EVENT_ID DESC

sqlfiddle aquí: http://sqlfiddle.com/#!4/81331/36

Me parece que los resultados coinciden con los tuyos. Dejé las columnas adicionales en la consulta final para intentar ilustrar cada paso.

Funciona en el caso de prueba: podría necesitar algún trabajo para manejar todas las posibilidades en un conjunto de datos más amplio, pero creo que este sería un buen lugar para comenzar y refinar.

El crédito para la combinación de rango superpuesto es la primera respuesta aquí: Combinar intervalos de fechas superpuestas

El crédito para list_agg con ventana es la primera respuesta aquí: LISTAGG equivalente con cláusula de ventana


Tuve un problema con sus "eventos en la carretera", debido a que no describe lo que es 1st meas , creo que es un período entre 0 y 1 sin 1.

así, puedes contar esto con una consulta:

with newest_MEAS as ( select ROAD_ID, MEAS.m, max(year) y from road_events join (select rownum -1 m from dual connect by rownum -1 <= (select max(TOTAL_ROAD_LENGTH) from road_events) ) MEAS on MEAS.m between FROM_MEAS and TO_MEAS group by ROAD_ID, MEAS.m ) select re.event_id, nm.ROAD_ID, re.total_road_length, nm.y, count(nm.m) EVENT_LENGTH from newest_MEAS nm join road_events re on nm.ROAD_ID = re.ROAD_ID and nm.m between re.from_meas and re.to_meas -1 and nm.y = re.year group by re.event_id, nm.ROAD_ID, re.total_road_length, nm.y order by event_id

SQL Fiddle


Solución:

SELECT RE.road_id, RE.event_id, RE.year, RE.from_meas, RE.to_meas, RE.road_length, RE.event_length, RE.used_length, RE.leftover_length FROM ( SELECT RE.C_road_id[road_id], RE.C_event_id[event_id], RE.C_year[year], RE.C_from_meas[from_meas], RE.C_to_meas[to_meas], RE.C_road_length[road_length], RE.event_length, RE.used_length, (RE.event_length - (CASE WHEN RE.HasOverlap = 1 THEN RE.used_length ELSE 0 END))[leftover_length] FROM ( SELECT RE.C_road_id, RE.C_event_id, RE.C_year, RE.C_from_meas, RE.C_to_meas, RE.C_road_length, (CASE WHEN MAX(RE.A_event_id) IS NOT NULL THEN 1 ELSE 0 END)[HasOverlap], (RE.C_to_meas - RE.C_from_meas)[event_length], SUM( (CASE WHEN RE.O_to_meas <= RE.C_to_meas THEN RE.O_to_meas ELSE RE.C_to_meas END) - (CASE WHEN RE.O_from_meas >= RE.C_from_meas THEN RE.O_from_meas ELSE RE.C_from_meas END) )[used_length]--This is the length that is already being counted towards later years. FROM ( SELECT RE.C_road_id, RE.C_event_id, RE.C_year, RE.C_from_meas, RE.C_to_meas, RE.C_road_length, RE.A_event_id, MIN(RE.O_from_meas)[O_from_meas], MAX(RE.O_to_meas)[O_to_meas] FROM ( SELECT RE_C.road_id[C_road_id], RE_C.event_id[C_event_id], RE_C.year[C_year], RE_C.from_meas[C_from_meas], RE_C.to_meas[C_to_meas], RE_C.total_road_length[C_road_length], RE_A.road_id[A_road_id], RE_A.event_id[A_event_id], RE_A.year[A_year], RE_A.from_meas[A_from_meas], RE_A.to_meas[A_to_meas], RE_A.total_road_length[A_road_length], RE_O.road_id[O_road_id], RE_O.event_id[O_event_id], RE_O.year[O_year], RE_O.from_meas[O_from_meas], RE_O.to_meas[O_to_meas], RE_O.total_road_length[O_road_length], (ROW_NUMBER() OVER (PARTITION BY RE_C.road_id, RE_C.event_id, RE_O.event_id ORDER BY RE_S.Overlap DESC, RE_A.event_id))[RowNum]--Use to Group Overlaps into Swaths. FROM road_events as RE_C--Current. LEFT JOIN road_events as RE_A--After. --Use a Left-Join to capture when there is only 1 Event (or it is the Last-Event in the list). ON RE_A.road_id = RE_C.road_id AND RE_A.event_id != RE_C.event_id--Not the same EventID. AND RE_A.year >= RE_C.year--Occured on or After the Current Event. AND ( (RE_A.from_meas >= RE_C.from_meas AND RE_A.from_meas <= RE_C.to_meas)--There is Overlap. OR (RE_A.to_meas >= RE_C.from_meas AND RE_A.to_meas <= RE_C.to_meas)--There is Overlap. OR (RE_A.to_meas = RE_C.to_meas AND RE_A.from_meas = RE_C.from_meas)--They are Equal. ) LEFT JOIN road_events as RE_O--Overlapped/Linked. ON RE_O.road_id = RE_C.road_id AND RE_O.event_id != RE_C.event_id--Not the same EventID. AND RE_O.year >= RE_C.year--Occured on or After the Current Event. AND ( (RE_O.from_meas >= RE_A.from_meas AND RE_O.from_meas <= RE_A.to_meas)--There is Overlap. OR (RE_O.to_meas >= RE_A.from_meas AND RE_O.to_meas <= RE_A.to_meas)--There is Overlap. OR (RE_O.to_meas = RE_A.to_meas AND RE_O.from_meas = RE_A.from_meas)--They are Equal. ) OUTER APPLY ( SELECT COUNT(*)[Overlap] FROM road_events as RE_O--Overlapped/Linked. WHERE RE_O.road_id = RE_C.road_id AND RE_O.event_id != RE_C.event_id--Not the same EventID. AND RE_O.year >= RE_C.year--Occured on or After the Current Event. AND ( (RE_O.from_meas >= RE_A.from_meas AND RE_O.from_meas <= RE_A.to_meas)--There is Overlap. OR (RE_O.to_meas >= RE_A.from_meas AND RE_O.to_meas <= RE_A.to_meas)--There is Overlap. OR (RE_O.to_meas = RE_A.to_meas AND RE_O.from_meas = RE_A.from_meas)--They are Equal. ) ) AS RE_S--Swath of Overlaps. ) AS RE WHERE RE.RowNum = 1--Remove Duplicates and Select those that are in the biggest Swaths. GROUP BY RE.C_road_id, RE.C_event_id, RE.C_year, RE.C_from_meas, RE.C_to_meas, RE.C_road_length, RE.A_event_id ) AS RE GROUP BY RE.C_road_id, RE.C_event_id, RE.C_year, RE.C_from_meas, RE.C_to_meas, RE.C_road_length ) AS RE ) AS RE WHERE RE.leftover_length > 0--Filter out Events that had their entire Segments overlapped by a Later Event(s). ORDER BY RE.road_id, RE.year DESC, RE.event_id

SQL Fiddle:
http://sqlfiddle.com/#!18/2880b/1

Reglas agregadas / suposiciones / aclaraciones:
1.) Permitir la posibilidad de que event_id y road_id puedan ser de Guid o creados fuera de orden,
por lo tanto, el script no asume que los valores más altos o más bajos dan sentido a la relación de registros.
Por ejemplo:
Una ID de 1 y una ID de 2 no garantiza que la ID de 2 sea la más reciente (y viceversa).
Esto es para que la solución sea más general y menos "hacky".
2.) Filtre los eventos que tuvieron sus segmentos completos superpuestos por uno o más eventos posteriores.
Por ejemplo:
Si 2008 tuvo trabajo en 20-50 y 2009 tuvo trabajo en 10-60,
luego, el Evento de 2008 se filtraría porque su segmento completo se reinvirtió en 2009.

Datos de prueba adicionales:
Para garantizar que las soluciones no se ajusten solo al conjunto de datos dado,
He añadido un road_id de 6 al DataSet original, para golpear algunos casos más.

INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (16,6,2012,0,100,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (17,6,2013,68,69,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (18,6,2014,65,66,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (19,6,2015,62,63,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (20,6,2016,50,60,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (21,6,2017,30,40,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (22,6,2017,20,55,100); INSERT INTO road_events (event_id, road_id, year, from_meas, to_meas, total_road_length) VALUES (23,6,2018,0,25,100);

Resultados: ( con los 8 registros adicionales que agregué en verde )

Versión de la base de datos:
Esta solución es Oracle y SQL-Server Agnostic:
Debería funcionar tanto en SS2008 + como en Oracle 12c +.

Esta pregunta está etiquetada con Oracle 12c , pero no puedo utilizar el violín en línea sin registrarme.
así que lo probé en SQL-Server, pero la misma sintaxis debería funcionar en ambos.
Confío en Cross-Apply y Outer-Apply para la mayoría de mis consultas.
Oracle introdujo estas "Uniones" en 12c:
https://oracle-base.com/articles/12c/lateral-inline-views-cross-apply-and-outer-apply-joins-12cr1

Simplificado y Performant:
Esto utiliza:
• No hay subconsultas correlacionadas.
• Sin recursión.
• No hay CTE''s.
• No hay sindicatos.
• No hay funciones de usuario.

Índices:
Leí en uno de sus comentarios que había preguntado acerca de los índices.
Me gustaría agregar índices de 1 columna para cada uno de los campos principales en los que buscará y agrupará:
road_id , event_id y year .
Podría ver si este índice lo ayudaría (no sé cómo planea usar los datos):
Campos clave: road_id , event_id , year
Incluye: from_meas , to_meas

Título:
Es posible que desee considerar cambiar el nombre del título de esta pregunta a algo más que se pueda buscar, como:
" Agregue segmentos superpuestos para medir la longitud efectiva ".
Esto permitiría que la solución sea más fácil de encontrar para ayudar a otros con problemas similares.

Otros pensamientos:
Algo como esto sería útil en el recuento del tiempo total empleado en algo
con marcas de tiempo de inicio y parada superpuestas.