Caso de prueba
Primero, una forma más útil de presentar sus datos, o incluso mejor, en un sqlfiddle , listo para jugar con:
CREATE TEMP TABLE data(
system_measured int
, time_of_measurement int
, measurement int
);
INSERT INTO data VALUES
(1, 1, 5)
,(1, 2, 150)
,(1, 3, 5)
,(1, 4, 5)
,(2, 1, 5)
,(2, 2, 5)
,(2, 3, 5)
,(2, 4, 5)
,(2, 5, 150)
,(2, 6, 5)
,(2, 7, 5)
,(2, 8, 5);
Consulta simplificada
Dado que no está claro, asumo solo lo anterior como se indica.
A continuación, simplifiqué su consulta para llegar a:
WITH x AS (
SELECT *, CASE WHEN lag(measurement) OVER (PARTITION BY system_measured
ORDER BY time_of_measurement) = measurement
THEN 0 ELSE 1 END AS step
FROM data
)
, y AS (
SELECT *, sum(step) OVER(PARTITION BY system_measured
ORDER BY time_of_measurement) AS grp
FROM x
)
SELECT * ,row_number() OVER (PARTITION BY system_measured, grp
ORDER BY time_of_measurement) - 1 AS repeat_ct
FROM y
ORDER BY system_measured, time_of_measurement;
Ahora, si bien es bueno y brillante usar SQL puro, esto será mucho más rápido con una función plpgsql, porque puede hacerlo en un solo escaneo de tabla donde esta consulta necesita al menos tres escaneos.
Más rápido con la función plpgsql:
CREATE OR REPLACE FUNCTION x.f_repeat_ct()
RETURNS TABLE (
system_measured int
, time_of_measurement int
, measurement int, repeat_ct int
) LANGUAGE plpgsql AS
$func$
DECLARE
r data; -- table name serves as record type
r0 data;
BEGIN
-- SET LOCAL work_mem = '1000 MB'; -- uncomment an adapt if needed, see below!
repeat_ct := 0; -- init
FOR r IN
SELECT * FROM data d ORDER BY d.system_measured, d.time_of_measurement
LOOP
IF r.system_measured = r0.system_measured
AND r.measurement = r0.measurement THEN
repeat_ct := repeat_ct + 1; -- start new array
ELSE
repeat_ct := 0; -- start new count
END IF;
RETURN QUERY SELECT r.*, repeat_ct;
r0 := r; -- remember last row
END LOOP;
END
$func$;
Llamar:
SELECT * FROM x.f_repeat_ct();
Asegúrese de calificar en la tabla los nombres de sus columnas en todo momento en este tipo de función plpgsql, porque usamos los mismos nombres como parámetros de salida que tendrían prioridad si no se calificaran.
Miles de millones de filas
Si tienes miles de millones de filas , es posible que desee dividir esta operación. Cito el manual aquí:
Nota:La implementación actual de RETURN NEXT
y RETURN QUERY
almacena todo el conjunto de resultados antes de regresar de la función, como se explicó anteriormente. Eso significa que si una función PL/pgSQL produce un conjunto de resultados muy grande, el rendimiento puede ser deficiente:los datos se escribirán en el disco para evitar el agotamiento de la memoria, pero la función en sí no regresará hasta que se haya generado todo el conjunto de resultados. Una versión futura de PL/pgSQL podría permitir a los usuarios definir funciones de devolución de conjuntos que no tengan esta limitación. Actualmente, el punto en el que los datos comienzan a escribirse en el disco está controlado por la variable de configuración work_mem. Los administradores que tienen suficiente memoria para almacenar conjuntos de resultados más grandes en la memoria deben considerar aumentar este parámetro.
Considere calcular filas para un sistema a la vez o establezca un valor lo suficientemente alto para work_mem
para hacer frente a la carga. Siga el enlace proporcionado en la cita para obtener más información sobre work_mem.
Una forma sería establecer un valor muy alto para work_mem
con SET LOCAL
en su función, que solo es efectivo para la transacción actual. Agregué una línea comentada en la función. no configúrelo muy alto a nivel mundial, ya que esto podría destruir su servidor. Lee el manual.