# Tipos temporales: Agregación e indexación

## Agregación

Las funciones agregadas temporales generalizan las funciones agregadas tradicionales. Su semántica es que calculan el valor de la función en cada instante de la unión de las extensiones temporales de los valores a agregar. En contraste, recuerde que todas las otras funciones que manipulan tipos temporales calculan el valor de la función en cada instante de la intersección de las extensiones temporales de los argumentos.

Las funciones agregadas temporales son las siguientes:

- Para todos los tipos temporales, la función `tCount` generaliza la función traditional `count`. El conteo temporal se puede utilizar para calcular en cada momento el número de objetos disponibles (por ejemplo, el número de coches en un área).

- Para todos los tipos temporales, la función `extent` devuelve un cuadro delimitador que engloba un conjunto de valores temporales. Dependiendo del tipo de base, el resultado de esta función puede ser un `tstzspan`, un `tbox` o un `stbox`.

- Para el tipo booleano temporal, las funciones `tAnd` y `tOr` generalizan las funciones traditionales `and` y `or`.

- Para los tipos numéricos temporales hay dos tipos de funciones agregadas temporales. Las funciones `tmin`, `tMax`, `tSum` y `tAvg` generalizan las funciones traditionales `min`, `max`, `sum` y `avg`. Además, las funciones `wMin`, `wMax`, `wCount`, `wSum` y `wAvg` son versiones de ventana (o acumulativas) de las funciones tradicionales que, dado un intervalo de tiempo w, calculan el valor de la función en un instante t considerando los valores durante el intervalo [t-w, t]. Todas las funciones agregadas de ventana están disponibles para enteros temporales, mientras que para flotantes temporales sólo son significativos el mínimo y el máximo de ventana.

- Para el tipo texto temporal, las funciones `tMin` y `tMax` generalizan las funciones traditionales `min` y `max`.

- Finalmente, para puntos temporales, la función `tCentroid` generaliza la función `ST_Centroid` proporcionada por PostGIS. Por ejemplo, dado un conjunto de objetos que se mueven juntos (es decir, un convoy o una bandada), el centroide temporal producirá un punto temporal que representa en cada instante el centro geométrico (o el centro de masa) de todos los objetos en movimiento.

En los ejemplos que siguen, suponemos que las tablas `Department` y `Trip` contienen las dos tuplas introducidas en la ttype_examples.

- Conteo temporal

  `tCount(ttype) → {tintSeq,tintSeqSet}`

      SELECT tCount(NoEmps) FROM Department;
      -- {[1@2001-01-01, 2@2001-02-01, 1@2001-08-01, 1@2001-10-01)}

- Extensión del cuadro delimitador

  `extent(temp) → {tstzspan,tbox,stbox}`

      SELECT extent(noEmps) FROM Department;
      -- TBOX XT((4,12),[2001-01-01,2001-10-01])
      SELECT extent(Trip) FROM Trips;
      -- STBOX XT(((0,0),(3,3),[2001-01-01 08:00:00+01, 2001-01-01 08:20:00+01)))

- Y, o temporal

  `tAnd(tbool) → tbool`

  `tOr(tbool) → tbool`

      SELECT tAnd(NoEmps #> 6) FROM Department;
      -- {[t@2001-01-01, f@2001-04-01, f@2001-10-01)}
      SELECT tOr(NoEmps #> 6) FROM Department;
      -- {[t@2001-01-01, f@2001-08-01, f@2001-10-01)}

- Mínimo, máximo, suma y promedio temporal

  `tMin(ttype) → ttype`

  `tMax(ttype) → ttype`

  `tSum(tnumber) → {tnumberSeq,tnumberSeqSet}`

  `tAvg(tnumber) → {tfloatSeq,tfloatSeqSet}`

      SELECT tMin(NoEmps) FROM Department;
      -- {[10@2001-01-01, 4@2001-02-01, 6@2001-06-01, 6@2001-10-01)}
      SELECT tMax(NoEmps) FROM Department;
      -- {[10@2001-01-01, 12@2001-04-01, 6@2001-08-01, 6@2001-10-01)}
      SELECT tSum(NoEmps) FROM Department;
      /* {[10@2001-01-01, 14@2001-02-01, 16@2001-04-01, 18@2001-06-01, 6@2001-08-01,
         6@2001-10-01)} */
      SELECT tAvg(NoEmps) FROM Department;
      /* {[10@2001-01-01, 10@2001-02-01), [7@2001-02-01, 7@2001-04-01),
         [8@2001-04-01, 8@2001-06-01), [9@2001-06-01, 9@2001-08-01),
         [6@2001-08-01, 6@2001-10-01) */

- Conteo de ventana

  `wCount(tnumber,interval) → {tintSeq,tintSeqSet}`

      SELECT wCount(NoEmps, interval '2 days') FROM Department;
      /* {[1@2001-01-01, 2@2001-02-01, 3@2001-04-01, 2@2001-04-03, 3@2001-06-01, 2@2001-06-03,
         1@2001-08-03, 1@2001-10-03)} */

- Mínimo, máximo, suma y promedio de ventana

  `wMin(tnumber,interval) → {tnumberSeq,tnumberSeqSet}`

  `wMax(tnumber,interval) → {tnumberSeq,tnumberSeqSet}`

  `wSum(tint,interval) → {tintSeq,tintSeqSet}`

  `wAvg(tint,interval) → {tfloatDiscSeq,tfloatSeqSet}`

      SELECT wMin(NoEmps, interval '2 days') FROM Department;
      -- {[10@2001-01-01, 4@2001-04-01, 6@2001-06-03, 6@2001-10-03)}
      SELECT wMax(NoEmps, interval '2 days') FROM Department;
      -- {[10@2001-01-01, 12@2001-04-01, 6@2001-08-03, 6@2001-10-03)}
      SELECT wSum(NoEmps, interval '2 days') FROM Department;
      /* {[10@2001-01-01, 14@2001-02-01, 26@2001-04-01, 16@2001-04-03, 22@2001-06-01,
         18@2001-06-03, 6@2001-08-03, 6@2001-10-03)} */
      SELECT round(wAvg(NoEmps, interval '2 days'), 3) FROM Department;
      /*  Interp=Step;{[10@2001-01-01, 7@2001-02-01, 8.667@2001-04-01, 8@2001-04-03,
         7.333@2001-06-01, 9@2001-06-03, 6@2001-08-03, 6@2001-10-03)} */

- Centroide temporal

  `tCentroid(tgeompoint) → tgeompoint`

      SELECT tCentroid(Trip) FROM Trips;
      /* {[POINT(0 0)@2001-01-01 08:00:00+00, POINT(1 0)@2001-01-01 08:05:00+00),
         [POINT(0.5 0)@2001-01-01 08:05:00+00, POINT(1.5 0.5)@2001-01-01 08:10:00+00,
         POINT(2 1.5)@2001-01-01 08:15:00+00),
         [POINT(2 2)@2001-01-01 08:15:00+00, POINT(3 3)@2001-01-01 08:20:00+00)} */

## Indexación

Se pueden crear índices GiST y SP-GiST para columnas de tabla de tipos temporales. El índice GiST implementa un árbol R, mientras que el índice SP-GiST implementa un árbol cuádruple n-dimensional. Ejemplos de creación de índices son los siguientes:

    CREATE INDEX Department_NoEmps_Gist_Idx ON Department USING Gist(NoEmps);
    CREATE INDEX Trips_Trip_SPGist_Idx ON Trips USING SPGist(Trip);

Los índices GiST y SP-GiST almacenan el cuadro delimitador para los tipos temporales. Como se explica en el ttype_p1, estos son

- el tipo `tstzspan` para los tipos `tbool` y `ttext`,

- el tipo `tbox` par los tipos `tint` y `tfloat`,

- el tipo `stbox` para los tipos `tgeompoint` y `tgeogpoint`.

Un índice GiST o SP-GiST puede acelerar las consultas que involucran a los siguientes operadores (consulte la ttype_bbox para obtener más información):

- `<<`, `&<`, `&>`, `>>`, que sólo consideran la dimensión de valores en tipos alfanuméricos temporales,

- `<<`, `&<`, `&>`, `>>`, `<<|`, `&<|`, `|&>`, `|>>`, `&</`, `<</`, `/>>` y `/&>`, que sólo consideran la dimensión espacial en tipos de puntos temporales,

- `&<#`, `<<#`, `#>>`, `#&>`, que sólo consideran la dimensión temporal para todos los tipos temporales,

- `&&`, `@>`, `<@`, `~=` y `|=|`, que consideran tantas dimensiones como compartan la columna indexada y el argumento de consulta. Estos operadores trabajan en cuadros delimitadores (es decir, `tstzspan`, `tbox` o `stbox`), no los valores completos.

Por ejemplo, dado el índice definido anteriormente en la tabla `Department` y una consulta que implica una condición con el operador `&&` (superposición), si el argumento derecho es un flotante temporal, entonces se consideran tanto el valor como las dimensiones de tiempo para filtrar las tuplas de la relación, mientras que si el argumento derecho es un valor flotante, un rango flotante o un tipo de tiempo, entonces el valor o la dimensión de tiempo se utilizará para filtrar las tuplas de la relación. Además, se puede construir un cuadro delimitador a partir de un valor/rango y/o una marca de tiempo/período, que se puede usar para filtrar las tuplas de la relación. Ejemplos de consultas que utilizan el índice en la tabla `Department` definida anteriormente se dan a continuación.

    SELECT * FROM Department WHERE NoEmps && intspan '[1, 5)';
    SELECT * FROM Department WHERE NoEmps && tstzspan '[2001-04-01, 2001-05-01)';
    SELECT * FROM Department WHERE NoEmps &&
      tbox(intspan '[1, 5)', tstzspan '[2001-04-01, 2001-05-01)');
    SELECT * FROM Department WHERE NoEmps &&
      tfloat '{[1@2001-01-01, 1@2001-02-01), [5@2001-04-01, 5@2001-05-01)}';

Del mismo modo, los ejemplos de consultas que utilizan el índice en la tabla `Trips` definida anteriormente se dan a continuación.

    SELECT * FROM Trips WHERE Trip && geometry 'Polygon((0 0,0 1,1 1,1 0,0 0))';
    SELECT * FROM Trips WHERE Trip && timestamptz '2001-01-01';
    SELECT * FROM Trips WHERE Trip && tstzspan '[2001-01-01, 2001-01-05)';
    SELECT * FROM Trips WHERE Trip &&
      stbox(geometry 'Polygon((0 0,0 1,1 1,1 0,0 0))', tstzspan '[2001-01-01, 2001-01-05]');
    SELECT * FROM Trips WHERE Trip &&
      tgeompoint '{[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02, Point(1 1)@2001-01-05)}';

Finalmente, se pueden crear índices de árbol B para columnas de tabla de todos los tipos temporales. Para este tipo de índice, la única operación útil es la igualdad. Hay un orden de clasificación de árbol B definido para valores de tipos temporales, con los correspondientes operadores `<`, `<=`, `>` y `>=`, pero el orden es bastante arbitrario y no suele ser útil en el mundo real. El soporte de árbol B para tipos temporales está destinado principalmente a permitir la clasificación interna en las consultas, en lugar de la creación de índices reales.

Para acelerar algunas de las funciones de los tipos temporales, se puede agregar en la cláusula `WHERE` de las consultas una comparación de cuadro delimitador que hace uso de los índices disponibles. Por ejemplo, este sería típicamente el caso de las funciones que proyectan los tipos temporales a las dimensiones de valor/espacio y/o tiempo. Esto filtrará las tuplas con un índice como se muestra en la siguiente consulta.

    SELECT atTime(T.Trip, tstzspan '[2001-01-01, 2001-01-02)')
    FROM Trips T
    -- Filtro de índice con cuadro delimitador
    WHERE T.Trip && tstzspan '[2001-01-01, 2001-01-02)';

En el caso de los geometrías temporales, todas las relaciones espaciales con la semántica alguna vez o siempre (ver la tgeo_spatial_rel) incluyen automáticamente una comparación de cuadro delimitador que hará uso de cualquier índice que esté disponible en los puntos temporales. Por esta razón, la primera versión de las relaciones se usa típicamente para filtrar las tuplas con la ayuda de un índice al calcular las relaciones temporales como se muestra en la siguiente consulta.

    SELECT tIntersects(T.Trip, R.Geom)
    FROM Trips T, Regions R
    -- Filtro de índice con cuadro delimitador
    WHERE eIntersects(T.Trip, R.Geom);

## Estadísticas y selectividad

### Colecta de estadísticas

El planificador de PostgreSQL se basa en información estadística sobre el contenido de las tablas para generar el plan de ejecución más eficiente para las consultas. Estas estadísticas incluyen una lista de algunos de los valores más comunes en cada columna y un histograma que muestra la distribución aproximada de datos en cada columna. Para tablas grandes, se toma una muestra aleatoria del contenido de la tabla, en lugar de examinar cada fila. Esto permite analizar tablas grandes en poco tiempo. La información estadística es recopilada por el comando `ANALYZE` y es almacenada en la tabla de catálogo `pg_statistic`. Dado que diferentes tipos de estadísticas pueden ser apropiados para diferentes tipos de datos, la tabla sólo almacena estadísticas muy generales (como el número de valores nulos) en columnas dedicadas. Todo lo demás se almacena en cinco “slots”, que son pares de columnas de matriz que almacenan las estadísticas de una columna de un tipo arbitrario.

Las estadísticas recopiladas para tipos de tiempo y tipos temporales se basan en las recopiladas por PostgreSQL para tipos escalares y tipos de rango. Para tipos escalares, como `float`, se recopilan las siguientes estadísticas:

1. fracción de valores nulos,

2. ancho promedio, en bytes, de valores no nulos,

3. número de diferentes valores no nulos,

4. matriz de los valores más comunes y matriz de sus frecuencias,

5. histograma de valores, donde se excluyen los valores más comunes,

6. correlación entre el orden de filas físico y lógico.

Para los tipos de rango, como `tstzspan`, se recopilan tres histogramas adicionales:

1. histograma de longitud de rangos no vacíos,

2. histogramas de límites superior e inferior.

Para geometrías, además de (1)–(3), se recopilan las siguientes estadísticas:

1. número de dimensiones de los valores, cuadro delimitador N-dimensional, número de filas en la tabla, número de filas en la muestra, número de valores no nulos,

2. Histograma N-dimensional que divide el cuadro delimitador en varias celdas y mantiene la proporción de valores que se cruzan con cada celda.

Las estadísticas recopiladas para columnas de los tipos de tiempo y de rango `tstzset`, `tstzspan`, `tstzspanset`, `intspan` y `floatspan` replican las recopiladas por PostgreSQL para `tstzrange`. Esto es claro para los tipos de rango en MobilityDB, que son versiones más eficientes de los tipos de rango en PostgreSQL. Para los tipos `tstzset` y `tstzspanset`, un valor se convierte en su período delimitador y luego se recopilan las estadísticas del tipo `tstzspan`.

Las estadísticas recopiladas para columnas de los tipos temporales dependen del subtipo temporal y del tipo base. Además de las estadísticas (1)–(3) que se recopilan para todos los tipos temporales, las estadísticas se recopilan para la dimensiónes de tiempo y de valor de forma independiente. Más precisamente, se recopilan las siguientes estadísticas para la dimensión de tiempo:

- Para columnas de subtipo instante, las estadísticas (4)–(6) se recopilan para las marcas de tiempo.

- Para columnas de otro subtipo, las estadísticas (7)–(8) se recopilan para los períodos delimitadores.

Las siguientes estadísticas se recopilan para la dimensión de valores:

- Para columnas de tipos temporales con interpolación escalonada (es decir, `tbool`, `ttext` o `tint`):

  - Para el subtipo instante, las estadísticas (4)–(6) se recopilan para los valores.

  - Para todas los demás subtipos, las estadísticas (7)–(8) se recopilan para los valores.

- Para columnas de tipos temporales flotantes (es decir, `tfloat`):

  - Para el subtipo instante, las estadísticas (4)–(6) se recopilan para los valores.

  - Para todas los demás subtipos, las estadísticas (7)–(8) se recopilan por los rangos delimitadores de valores.

- Para columnas de tipos de puntos temporales (es decir, `tgeompoint` y `tgeogpoint`) las estadísticas (9)–(10) se compilan para los puntos.

### Estimación de la selectividad

Los operadores booleanos en PostgreSQL se pueden asociar con dos funciones de selectividad, que calculan la probabilidad de que un valor de un tipo dado coincida con un criterio dado. Estas funciones de selectividad se basan en las estadísticas recopiladas. Hay dos tipos de funciones de selectividad. Las funciones de selectividad de restricción intentan estimar el porcentaje de filas en una tabla que satisfacen una condición en la cláusula `WHERE` de la forma `column OP constant`. Por otro lado, las funciones de selectividad de unión intentan estimar el porcentaje de filas en una tabla que satisfacen una condición en la cláusula `WHERE` de la forma `table1.column1 OP table2.column2`.

MobilityDB define 23 clases de operadores booleanos (como`=`, `<`, `&&`, `<<`, etc.), cada uno de los cuales puede tener como argumentos izquierdo o derecho un tipo PostgreSQL (como `integer`, `timestamptz`, etc.) o un tipo MobilityDB (como `tstzspan`, `tint`, etc.). Como consecuencia, existe un número muy elevado de operadores con diferentes argumentos a considerar para las funciones de selectividad. El enfoque adoptado fue agrupar estas combinaciones en clases correspondientes a las dimensiones de valor o de tiempo. Las clases corresponden al tipo de estadísticas recopiladas como se explica en la sección anterior.

MobilityDB estima la selectividad de restricción y de combinación para tipos de tiempo, de rango y temporales.