# Referencia de Probara Query Language (PQL)

> Cada entidad, campo, operador, función, límite y error de Probara Query Language (PQL), con ejemplos resueltos para preguntas comunes de QA.

**Probara Query Language (PQL)** hace preguntas sobre tus datos de prueba. Una consulta indica una entidad, como casos o resultados, filtra sus filas con `WHERE`, y puede agruparlas y agregarlas. Escribe PQL en [Consultas](https://docs.probara.net/es/guides/queries/) o envíalo a la [API de PQL](https://docs.probara.net/es/developers/api/pql/).

## Inicio rápido

1. Indica la entidad: `FROM cases`.
2. Filtra las filas: `WHERE priority = "High"`.
3. Elige el tamaño de página: `LIMIT 50`.

```text
FROM cases WHERE priority = "High" AND status = "Active" ORDER BY title LIMIT 50
```

Los ejemplos usan los nombres en inglés de las opciones predeterminadas, como `High` o `Active`. Si tu organización usa otros nombres, escribe los suyos.

## Forma de la consulta

| Cláusula           | Para qué sirve                     | Notas                                                                                |
| ------------------ | ---------------------------------- | ------------------------------------------------------------------------------------ |
| `SELECT`           | Columnas y agregados               | Opcional. Sin ella, la consulta devuelve las columnas predeterminadas de la entidad. |
| `FROM`             | La entidad                         | Obligatoria. Una de las nueve entidades de [Entidades](#entidades).                  |
| `WHERE`            | Condiciones sobre cada fila        | Se aplica antes de agrupar.                                                          |
| `GROUP BY`         | Grupos                             | Campos, expresiones o posiciones, como `GROUP BY 1`.                                 |
| `HAVING`           | Condiciones sobre los grupos       | Usa agregados, como `HAVING count() > 5`.                                            |
| `ORDER BY`         | Orden                              | Campos, alias, posiciones o agregados. Agrega `DESC` para orden descendente.         |
| `LIMIT` y `OFFSET` | Tamaño de página y punto de inicio | `LIMIT` vale 50 por defecto y admite hasta 500. `OFFSET` va después de `LIMIT`.      |

Reglas:

- Las palabras clave, los nombres de función, de entidad y de campo no distinguen mayúsculas de minúsculas.
- Un comentario de línea empieza con `--`.
- Los saltos de línea y los espacios extra no importan.
- Una consulta con `SELECT` y un agregado devuelve filas agrupadas. Cualquier otra consulta devuelve filas.
- Un elemento de `SELECT` que no está agregado debe aparecer en `GROUP BY`.

## Literales

| Tipo                          | Cómo se escribe                                                                                                                    | Ejemplo                                |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| Texto                         | Entre comillas dobles o simples.                                                                                                   | `title ~ "login"`                      |
| Número                        | Un entero o un decimal.                                                                                                            | `steps >= 3`                           |
| Booleano                      | `true` o `false`.                                                                                                                  | `archived = false`                     |
| Lista                         | Valores entre paréntesis, separados por comas. Como máximo 200 valores.                                                            | `priority in ("High", "Critical")`     |
| Duración fija                 | Un número y una unidad: `m` minutos, `h` horas, `d` días, `w` semanas.                                                             | `duration > 30m`                       |
| Duración relativa             | Una duración con signo dentro de una función de tiempo. Las unidades también incluyen `mo` (meses), `q` (trimestres) e `y` (años). | `now(-3mo)`, `startOf(month, -1mo)`    |
| Fecha                         | `"YYYY-MM-DD"` o `"YYYY-MM-DD HH:mm"`, entre comillas.                                                                             | `created > "2026-10-01"`               |
| Valor de enumeración u opción | Una palabra suelta o un valor entre comillas. La comparación no distingue mayúsculas.                                              | `status = failed`, `priority = "High"` |

Notas:

- Los valores de texto deben ir entre comillas. `title = Login` es un error, y `title = "Login"` funciona.
- Los nombres de hitos, suites, entornos, etiquetas y proyectos son texto. Ponlos entre comillas cuando tengan espacios: `milestone = "Release 1.4"`.
- Los campos de duración fija, como `runs.duration`, aceptan solo `m`, `h`, `d` y `w`. Usa `mo`, `q` e `y` solo en el tiempo relativo.

## Operadores

| Operador                           | Qué hace                                                                                                      | Tipos de campo                                             |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `=`, `!=`                          | Igual, o distinto                                                                                             | Todos los campos, excepto `lastResult`                     |
| `<`, `<=`, `>`, `>=`               | Menor, menor o igual, mayor, mayor o igual                                                                    | Número, duración, fecha                                    |
| `between a and b`                  | Desde `a` hasta `b`                                                                                           | Número, duración, fecha                                    |
| `~`, `!~`                          | Contiene, o no contiene, sin distinguir mayúsculas                                                            | Texto                                                      |
| `startsWith`                       | Empieza por el texto                                                                                          | Texto                                                      |
| `in (...)`, `not in (...)`         | Uno de una lista, o ninguno de ella                                                                           | Todos los campos, excepto booleano y `lastResult`          |
| `in query("Nombre")`               | Coincide con las filas de una consulta guardada. Ver [Referencias con in query()](#referencias-con-in-query). | Solo `id`                                                  |
| `is empty`, `is not empty`         | Sin valor, o con valor                                                                                        | Campos marcados como opcionales en las tablas de entidades |
| `under "Suite"`                    | La suite y todas las suites que dependen de ella                                                              | Referencias a suites: `cases.suite`, `suites.parent`       |
| `exists(...)`, `count(...) <op> n` | Registros relacionados. Ver [Relaciones](#relaciones).                                                        | Entidades con relaciones                                   |
| `was`, `changed`                   | Historial de cambios. Ver [Historial](#historial).                                                            | Campos con historial                                       |

Los campos booleanos solo aceptan `=` y `!=`, así que escribe `archived = true`, no `archived`.

## Entidades

Cada entidad tiene un `id`, que es su ID visible, y un `project`. Las tablas listan los campos que puedes usar. Un campo marcado como **Opcional** puede estar vacío, así que `is empty` se aplica a él. Un campo marcado como **Historial** admite `was` y `changed`.

### Casos

Columnas predeterminadas: `id`, `title`, `suite`, `priority`, `status`.

| Campo           | Tipo                 | Significado                                                                                                                        | Historial |
| --------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------- |
| `id`            | ID                   | El ID visible del caso                                                                                                             |           |
| `project`       | Referencia           | El proyecto                                                                                                                        |           |
| `title`         | Texto                | El título                                                                                                                          |           |
| `description`   | Texto, opcional      | La descripción                                                                                                                     |           |
| `suite`         | Referencia, opcional | La suite. Usa `under` para su árbol.                                                                                               |           |
| `milestone`     | Referencia, opcional | El hito                                                                                                                            |           |
| `tags`          | Etiquetas, opcional  | Las etiquetas. Un caso cuenta en el grupo de cada una de sus etiquetas.                                                            |           |
| `author`        | Usuario              | Quién creó el caso                                                                                                                 |           |
| `updatedBy`     | Usuario, opcional    | Quién actualizó el caso por última vez                                                                                             |           |
| `created`       | Fecha                | Cuándo se creó el caso                                                                                                             |           |
| `updated`       | Fecha                | Cuándo se actualizó el caso por última vez                                                                                         |           |
| `archived`      | Booleano             | Si el caso está archivado. Ver [Casos archivados](#casos-archivados).                                                              |           |
| `automationKey` | Texto, opcional      | La clave de automatización                                                                                                         |           |
| `automated`     | Booleano             | Si el caso tiene una clave de automatización                                                                                       |           |
| `steps`         | Número               | El número de pasos                                                                                                                 |           |
| `revision`      | Número               | El número de revisión                                                                                                              |           |
| `reviewStatus`  | Enum                 | `not_reviewed`, `in_review` o `approved`. Consulta [Revisión de casos de prueba](https://docs.probara.net/es/guides/test-case-review/#estado-de-revisión). |           |
| `jira`          | Texto, opcional      | La primera clave de incidencia de Jira vinculada                                                                                   |           |
| `priority`      | Opción, opcional     | La prioridad, de las opciones de tu organización                                                                                   | Sí        |
| `severity`      | Opción, opcional     | La severidad, de las opciones de tu organización                                                                                   | Sí        |
| `status`        | Opción, opcional     | El estado, de las opciones de tu organización                                                                                      | Sí        |
| `type`          | Opción, opcional     | El tipo, de las opciones de tu organización                                                                                        | Sí        |
| `layer`         | Opción, opcional     | La capa, de las opciones de tu organización                                                                                        | Sí        |
| `behavior`      | Opción, opcional     | El comportamiento, de las opciones de tu organización                                                                              | Sí        |
| `isFlaky`       | Booleano             | Si el caso está marcado como inestable                                                                                             |           |
| `lastResult`    | Resultado            | El resultado más reciente. Usa `lastResult.campo`. Ver [lastResult](#lastresult).                                                  |           |

Relaciones: `results`, `runCases`, `defects`, `jira`. Campos personalizados: `cf[...]`.

### Suites

Columnas predeterminadas: `id`, `name`, `parent`, `cases`.

| Campo     | Tipo                 | Significado                                 |
| --------- | -------------------- | ------------------------------------------- |
| `id`      | ID                   | El ID visible de la suite                   |
| `project` | Referencia           | El proyecto                                 |
| `name`    | Texto                | El nombre                                   |
| `parent`  | Referencia, opcional | La suite padre. Usa `under` para su árbol.  |
| `path`    | Texto                | La ruta de la suite                         |
| `cases`   | Número               | El número de casos que no están archivados  |
| `created` | Fecha                | Cuándo se creó la suite                     |
| `updated` | Fecha                | Cuándo se actualizó la suite por última vez |

Relaciones: `cases`.

### Ejecuciones

Columnas predeterminadas: `id`, `name`, `state`, `status`, `milestone`, `passRate`.

| Campo                                                | Tipo                 | Significado                                                                                                                                                                      |
| ---------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                                 | ID                   | El ID visible de la ejecución                                                                                                                                                    |
| `project`                                            | Referencia           | El proyecto                                                                                                                                                                      |
| `name`                                               | Texto                | El nombre                                                                                                                                                                        |
| `state`                                              | Enumeración          | `open` o `closed`                                                                                                                                                                |
| `status`                                             | Enumeración          | `in_progress`, `passed`, `failed` o `aborted`. Una ejecución abierta está en progreso, y una abortada está abortada.                                                             |
| `environment`                                        | Referencia, opcional | El entorno                                                                                                                                                                       |
| `milestone`                                          | Referencia, opcional | El hito                                                                                                                                                                          |
| `plan`                                               | Referencia, opcional | El plan de pruebas                                                                                                                                                               |
| `author`                                             | Usuario              | Quién creó la ejecución                                                                                                                                                          |
| `tags`                                               | Etiquetas, opcional  | Las etiquetas                                                                                                                                                                    |
| `started`, `finished`, `closed`, `aborted`           | Fecha, opcional      | Cuándo empezó, terminó, se cerró o se abortó la ejecución                                                                                                                        |
| `duration`                                           | Duración, opcional   | El tiempo total de la ejecución                                                                                                                                                  |
| `passed`, `failed`, `blocked`, `skipped`, `untested` | Número               | El número de casos con cada resultado                                                                                                                                            |
| `passRate`                                           | Número               | La proporción de casos ejecutados que aprobaron, como porcentaje de 0 a 100, redondeado a un decimal. No se puede promediar ni sumar. Usa `passRate()` en una consulta agregada. |
| `branch`                                             | Texto, opcional      | La rama                                                                                                                                                                          |
| `commit`                                             | Texto, opcional      | El commit                                                                                                                                                                        |

Relaciones: `results`, `runCases`, `defects`. Campos personalizados: `cf[...]`.

### Casos de ejecución

Los casos de ejecución son los casos dentro de una ejecución, incluidos los que aún no se han probado. Columnas predeterminadas: `run`, `case`, `status`, `assignee`, `elapsed`.

| Campo        | Tipo               | Significado                                                                                                                                                                                                                                   |
| ------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | ID                 | El ID visible                                                                                                                                                                                                                                 |
| `project`    | Referencia         | El proyecto                                                                                                                                                                                                                                   |
| `run`        | Referencia         | La ejecución. Usa `run.campo` para sus campos.                                                                                                                                                                                                |
| `case`       | Referencia         | El caso. Usa `case.campo` para sus campos.                                                                                                                                                                                                    |
| `status`     | Enumeración        | `untested`, `retested`, `passed`, `failed`, `skipped` o `blocked`                                                                                                                                                                             |
| `assignee`   | Usuario, opcional  | A quién está asignado el caso                                                                                                                                                                                                                 |
| `elapsed`    | Duración, opcional | El tiempo dedicado al caso                                                                                                                                                                                                                    |
| `parameters` | Texto, opcional    | La combinación del caso de ejecución como pares nombre=valor unidos con ", ". `~` coincide con un par o con un valor, por ejemplo `FROM runCases WHERE parameters ~ "Browser=Firefox"`. Un caso de ejecución sin parámetros no tiene ninguno. |

Sin relaciones.

Un caso de ejecución de un caso parametrizado es una instancia: un caso con dos parámetros de tres valores cada uno tiene nueve casos de ejecución en una ejecución. Cuéntalos con `count()` sobre los casos de ejecución, y consulta [Casos de prueba parametrizados](https://docs.probara.net/es/guides/parameterized-test-cases/).

### Resultados

Columnas predeterminadas: `id`, `run`, `case`, `status`, `executedBy`, `executed`.

| Campo        | Tipo               | Significado                                    |
| ------------ | ------------------ | ---------------------------------------------- |
| `id`         | ID                 | El ID visible, como `RES-42`                   |
| `project`    | Referencia         | El proyecto                                    |
| `case`       | Referencia         | El caso. Usa `case.campo` para sus campos.     |
| `run`        | Referencia         | La ejecución. Usa `run.campo` para sus campos. |
| `status`     | Enumeración        | `passed`, `failed`, `skipped` o `blocked`      |
| `duration`   | Duración, opcional | Cuánto duró la ejecución                       |
| `executedBy` | Usuario, opcional  | Quién ejecutó el caso                          |
| `executed`   | Fecha              | Cuándo se ejecutó el caso                      |
| `comment`    | Texto, opcional    | El comentario                                  |

Sin relaciones. Campos personalizados: ninguno.

### Defectos

Columnas predeterminadas: `id`, `title`, `status`, `severity`, `priority`, `assignee`.

| Campo        | Tipo                  | Significado                                           | Historial |
| ------------ | --------------------- | ----------------------------------------------------- | --------- |
| `id`         | ID                    | El ID visible                                         |           |
| `project`    | Referencia            | El proyecto                                           |           |
| `title`      | Texto                 | El título                                             |           |
| `status`     | Enumeración           | `open`, `in_progress`, `resolved` o `closed`          | Sí        |
| `resolution` | Enumeración, opcional | `fixed`, `wont_fix`, `duplicate` o `cannot_reproduce` |           |
| `severity`   | Opción, opcional      | La severidad, de las opciones de tu organización      | Sí        |
| `priority`   | Opción, opcional      | La prioridad, de las opciones de tu organización      | Sí        |
| `reporter`   | Usuario               | Quién reportó el defecto                              |           |
| `assignee`   | Usuario, opcional     | A quién está asignado el defecto                      | Sí        |
| `milestone`  | Referencia, opcional  | El hito                                               |           |
| `tags`       | Etiquetas, opcional   | Las etiquetas                                         |           |
| `created`    | Fecha                 | Cuándo se creó el defecto                             |           |
| `updated`    | Fecha                 | Cuándo se actualizó el defecto por última vez         |           |
| `resolved`   | Fecha, opcional       | Cuándo se resolvió el defecto                         |           |
| `closed`     | Fecha, opcional       | Cuándo se cerró el defecto                            |           |
| `jira`       | Texto, opcional       | La clave de incidencia de Jira vinculada              |           |

Relaciones: `cases`, `results`. Campos personalizados: `cf[...]`.

### Hitos

Columnas predeterminadas: `id`, `name`, `status`, `due`.

| Campo       | Tipo                 | Significado                        |
| ----------- | -------------------- | ---------------------------------- |
| `id`        | ID                   | El ID visible                      |
| `project`   | Referencia           | El proyecto                        |
| `name`      | Texto                | El nombre                          |
| `status`    | Enumeración          | `upcoming`, `active` o `completed` |
| `parent`    | Referencia, opcional | El hito padre                      |
| `start`     | Fecha, opcional      | La fecha de inicio                 |
| `due`       | Fecha, opcional      | La fecha límite                    |
| `completed` | Fecha, opcional      | Cuándo se completó el hito         |

Relaciones: `runs`, `cases`, `defects`.

### Planes

Columnas predeterminadas: `id`, `name`, `status`, `milestone`.

| Campo       | Tipo                 | Significado                     |
| ----------- | -------------------- | ------------------------------- |
| `id`        | ID                   | El ID visible                   |
| `project`   | Referencia           | El proyecto                     |
| `name`      | Texto                | El nombre                       |
| `status`    | Enumeración          | `draft`, `active` o `completed` |
| `milestone` | Referencia, opcional | El hito                         |
| `created`   | Fecha                | Cuándo se creó el plan          |
| `updated`   | Fecha                | Cuándo se actualizó el plan     |

Relaciones: `runs`, `cases`.

### Pasos compartidos

Columnas predeterminadas: `id`, `title`, `folder`, `updated`.

| Campo     | Tipo                 | Significado                                                                |
| --------- | -------------------- | -------------------------------------------------------------------------- |
| `id`      | ID                   | El ID visible                                                              |
| `project` | Referencia, opcional | El proyecto. Si está vacío, el paso compartido es de toda la organización. |
| `title`   | Texto                | El título                                                                  |
| `folder`  | Referencia, opcional | La carpeta                                                                 |
| `created` | Fecha                | Cuándo se creó el paso compartido                                          |
| `updated` | Fecha                | Cuándo se actualizó el paso compartido por última vez                      |

Sin relaciones.

## Campos personalizados

Los casos, las ejecuciones y los defectos tienen los campos personalizados de tu organización. Escribe el título del campo entre corchetes:

```text
FROM cases WHERE cf["Risk"] = "High"
```

El título se compara sin distinguir mayúsculas y sin los espacios de los extremos. Un título que ningún campo personalizado de esa entidad tiene devuelve `unknown_custom_field`.

| Tipo de campo personalizado          | Funciona como | Operadores                                                                      |
| ------------------------------------ | ------------- | ------------------------------------------------------------------------------- |
| Número                               | Número        | Comparaciones, `between`, listas                                                |
| Texto corto, párrafo, URL            | Texto         | `~`, `!~`, `startsWith`, listas, `=`                                            |
| Casilla de verificación              | Booleano      | `=`, `!=`                                                                       |
| Selector de fecha                    | Fecha         | Comparaciones, `between`, listas                                                |
| Selector de usuario                  | Usuario       | `=`, listas y las funciones de miembros                                         |
| Selección, selección múltiple, radio | Opción        | `=`, listas. Una selección múltiple cuenta una fila en cada uno de sus valores. |

## Rutas a registros relacionados

Los resultados y los casos de ejecución apuntan a una ejecución y a un caso. Accede a sus campos con una ruta de puntos:

```text
FROM results WHERE run.environment = "staging" AND case.priority = "Critical" AND status = failed
```

Las rutas funcionan en `WHERE` y en `GROUP BY`, por ejemplo `GROUP BY run.environment`.

## lastResult

`lastResult` es el resultado más reciente de un caso, por fecha de ejecución y después por ID. Está disponible en `cases` en el nivel superior de una consulta.

- `lastResult.status` lee el estado del resultado más reciente.
- `lastResult(environment = "staging").status` lee el resultado más reciente entre las ejecuciones que cumplen la condición. La condición usa campos de ejecución, como `environment`, `milestone` y `plan`.
- Un caso sin resultado coincidente no tiene valor, así que `=` no lo encuentra.

```text
FROM cases WHERE lastResult(environment = "staging").status = failed ORDER BY title
```

`lastResult` no se puede comparar directamente, y no tiene historial.

## Relaciones

`exists` y `count` miran los registros relacionados de cada fila.

| Entidad      | Relaciones                               |
| ------------ | ---------------------------------------- |
| `cases`      | `results`, `runCases`, `defects`, `jira` |
| `suites`     | `cases`                                  |
| `runs`       | `results`, `runCases`, `defects`         |
| `milestones` | `runs`, `cases`, `defects`               |
| `plans`      | `runs`, `cases`                          |
| `defects`    | `cases`, `results`                       |
| Otras        | Ninguna                                  |

```text
FROM cases WHERE exists(results where status = failed)
FROM cases WHERE NOT exists(results)
FROM cases WHERE count(results where status = failed) >= 3
```

Reglas:

- Usa `exists` y `count` en `WHERE`. `exists` en `HAVING` devuelve `relation_placement`.
- Compara `count(relación)` con un número.
- Las subconsultas se anidan como máximo dos niveles. `FROM suites WHERE exists(cases where exists(results where status = failed))` tiene dos niveles.
- Los registros relacionados cuentan solo en los proyectos a los que tienes acceso, y cada registro relacionado cuenta una vez.
- Los casos archivados se dejan fuera de una condición de registros relacionados sobre casos, salvo que la condición nombre `archived`. Ver [Casos archivados](#casos-archivados).

## Funciones

| Función                      | Devuelve                                                                      | Notas                                                                                               |
| ---------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `currentUser()`              | El usuario que ha iniciado sesión                                             | Úsala en campos de usuario y en `by`.                                                               |
| `membersOf("Grupo")`         | Los miembros de un grupo                                                      | Úsala dentro de `in (...)` en un campo de usuario.                                                  |
| `activeMembers()`            | Los miembros actuales de la organización                                      | Úsala dentro de `in (...)` en un campo de usuario.                                                  |
| `formerMembers()`            | Los antiguos miembros de la organización                                      | Úsala dentro de `in (...)` en un campo de usuario.                                                  |
| `now([offset])`              | La hora actual, desplazada si se indica                                       | El desplazamiento es una duración con signo, como `now(-7d)`.                                       |
| `startOf(unit[, offset])`    | El inicio de la hora, el día, la semana, el mes, el trimestre o el año actual | `startOf(week)`, `startOf(month, -1mo)`                                                             |
| `endOf(unit[, offset])`      | El final de las mismas unidades                                               | `endOf(day)`                                                                                        |
| `count()`                    | El número de filas de cada grupo                                              | `count(*)` también funciona.                                                                        |
| `count(distinct campo)`      | El número de valores distintos                                                | `count(distinct executedBy)`                                                                        |
| `sum(campo)`, `avg(campo)`   | La suma, o el promedio, de un número o una duración                           | `avg(duration)`                                                                                     |
| `min(campo)`, `max(campo)`   | El valor más pequeño, o el más grande                                         | Números, duraciones y fechas.                                                                       |
| `passRate()`                 | El porcentaje de resultados aprobados                                         | Solo en resultados y ejecuciones. De 0 a 100, redondeado a un decimal. Vacío si no se ejecutó nada. |
| `bucket(campoDeFecha, unit)` | El inicio del intervalo de tiempo en el que cae una fecha                     | Unidades: `hour`, `day`, `week`, `month`, `quarter`, `year`. Las semanas empiezan el lunes.         |

`passRate()` cuenta así:

- En resultados, los resultados aprobados divididos entre todos los resultados.
- En ejecuciones, los casos aprobados divididos entre los casos aprobados, fallidos, bloqueados y omitidos. Es la misma cifra que `runs.passRate`.

```text
SELECT bucket(executed, week) AS period, passRate() AS rate, count() AS results FROM results GROUP BY 1 ORDER BY 1
```

## Tiempo y zonas horarias

Las fechas y los tiempos relativos se leen en la **Zona horaria** de tu [perfil](https://docs.probara.net/es/guides/user-profile/). Con **Detectado automáticamente**, se usa UTC. `now`, `startOf`, `endOf`, `bucket` y las comparaciones de fechas siguen esta zona.

El tiempo relativo admite todas las unidades: `m`, `h`, `d`, `w`, `mo`, `q` e `y`. Los campos de duración fija admiten `m`, `h`, `d` y `w`.

## Reglas de agregación

- **`GROUP BY`** acepta campos, expresiones y posiciones, como `GROUP BY 1`. No acepta alias de `SELECT`. Repite la expresión, o usa su posición.
- **`HAVING`** filtra grupos con agregados, como `HAVING count() > 5`. No acepta alias, así que `HAVING n > 5` falla.
- **`ORDER BY`** acepta alias (`ORDER BY n DESC`), posiciones contadas desde 1 (`ORDER BY 2`) y agregados (`ORDER BY count() DESC`). Los agregados necesitan un grupo, o un agregado en `SELECT`. Los campos de usuario y de etiquetas no se pueden ordenar.
- **La agrupación** funciona en campos de número, booleano, enumeración, opción, usuario, referencia y etiquetas. El texto y las fechas no se agrupan directamente. Agrupa una fecha con `bucket()`.
- **Las etiquetas y los campos de selección múltiple** cuentan una fila en el grupo de cada uno de sus valores. Si agrupas dos de estos campos a la vez, hay un grupo por cada combinación, así que los grupos pueden sumar más que el número de filas.
- **Sin valor.** Las filas sin valor forman un grupo llamado **Sin valor**. Aparece primero.
- **`count(campo)`** cuenta las filas en las que el campo tiene valor.
- **`HAVING` sobre una etiqueta o un campo de selección múltiple** se rechaza. Filtra en `WHERE`.
- **Sin `GROUP BY`**, un agregado devuelve una fila para todas las filas que coinciden.
- **Los intervalos de tiempo** empiezan al inicio de la unidad en tu zona horaria. Los intervalos vacíos no se devuelven.
- **Los selectores de fecha** comparan por día local.
- **Los grupos** se muestran hasta 1.000 por página. El resultado lo indica cuando hay más.

## Historial

El historial de cambios registra los cambios de algunos campos. Dos operadores lo leen:

- `status was in_progress` es verdadero cuando el valor es el actual, o era uno de los valores registrados en el historial.
- `status changed` es verdadero cuando el historial tiene al menos un cambio. Agrega modificadores para acotarlo:

| Modificador  | Significado                                                          |
| ------------ | -------------------------------------------------------------------- |
| `from v`     | El cambio partió de `v`.                                             |
| `to v`       | El cambio terminó en `v`.                                            |
| `by usuario` | El cambio lo hizo `currentUser()`, o un miembro de `membersOf(...)`. |
| `after t`    | El cambio ocurrió después de `t`.                                    |
| `before t`   | El cambio ocurrió antes de `t`.                                      |

Todos los modificadores que escribas deben cumplirse en el mismo cambio. `t` es una fecha o un tiempo relativo, que se lee en tu zona horaria.

```text
FROM defects WHERE status changed from resolved to open after now(-14d)
FROM cases WHERE priority changed by currentUser() after now(-7d)
```

### Campos con historial

| Entidad   | Campos                                                        |
| --------- | ------------------------------------------------------------- |
| `cases`   | `priority`, `severity`, `status`, `type`, `layer`, `behavior` |
| `defects` | `status`, `assignee`, `severity`, `priority`                  |

El historial de cambios no está disponible para el estado de las ejecuciones, los movimientos entre hitos y suites, los campos personalizados (`cf[...]`) ni los campos vinculados. Una consulta que los use devuelve `history_not_available`.

Reglas:

- Usa `was` y `changed` en `WHERE`. En `HAVING` devuelven `history_placement`.
- El historial de cambios guarda registros durante la ventana de retención de historial de tu plan. Consulta [Retención de datos](https://docs.probara.net/es/guides/test-runs/#retención-de-datos).
- Los cambios hechos antes de que el registro fuera auditado no se conocen, así que un valor puede faltar en su historial.

## Referencias con in query()

`id in query("Nombre de la consulta")` conserva las filas que devuelve una consulta guardada:

```text
FROM cases WHERE id in query("Critical failures")
```

- La consulta guardada debe devolver la misma entidad, y en modo de filas.
- Se ignoran su `ORDER BY` y su `LIMIT`.
- Una consulta guardada puede referirse a otra hasta tres niveles de profundidad. Un ciclo se rechaza.
- Solo puedes referirte a consultas que puedes ver. Un nombre desconocido u oculto devuelve `unknown_saved_query`.

## Casos archivados

Una consulta sobre casos deja fuera los casos archivados, salvo que la consulta nombre `archived` en su propio nivel. La misma regla se aplica a una condición de registros relacionados sobre casos, como `exists(cases ...)`.

```text
FROM cases WHERE title ~ "login"                 -- los casos archivados quedan fuera
FROM cases WHERE archived = true                 -- la condición decide
```

Una consulta sobre resultados es distinta. Archivar conserva el historial de ejecución de un caso, así que `FROM results` (y `FROM runCases`) incluye los resultados de los casos archivados. Para dejarlos fuera, indica que el caso está activo:

```text
FROM results WHERE case.archived = false         -- los resultados de los casos archivados quedan fuera
```

## Límites

| Límite                                         | Valor                                                 |
| ---------------------------------------------- | ----------------------------------------------------- |
| Texto de la consulta                           | 4.000 caracteres                                      |
| Valores en una lista `in (...)`                | 200                                                   |
| `LIMIT` (filas por página)                     | 50 por defecto, 500 como máximo                       |
| `OFFSET`                                       | 10.000 como máximo                                    |
| Filas contadas                                 | Exacto hasta 10.000; después **10.000+**              |
| Grupos por página                              | 1.000 como máximo                                     |
| Anidamiento de relaciones (`exists` y `count`) | Dos niveles                                           |
| Referencias en `in query()`                    | Tres niveles. Los ciclos se rechazan.                 |
| Exportación CSV                                | 10.000 filas, o 1.000 grupos en una consulta agregada |
| Nombre de una consulta guardada                | 120 caracteres, único en la organización              |
| Descripción de una consulta guardada           | 2.000 caracteres                                      |

## Errores

Cada error tiene un código, un mensaje y el lugar de la consulta donde ocurre. A través de la API, cada error aparece en `details.errors` con el mismo código. Consulta [Errores](https://docs.probara.net/es/developers/api/pql/#errores).

### Errores de sintaxis

| Código                    | Qué significa                                                                                                                           | Qué hacer                                                |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| `unexpected_character`    | La consulta tiene un carácter que PQL no usa, como `#`.                                                                                 | Quítalo, o ponlo entre comillas si es texto.             |
| `unterminated_string`     | Una comilla no está cerrada.                                                                                                            | Añade la comilla de cierre.                              |
| `missing_from`            | La consulta no empieza con `FROM`.                                                                                                      | Empieza la consulta con `FROM` y una entidad.            |
| `unexpected_token`        | Una palabra o un símbolo está en un lugar incorrecto. También aparece si una cláusula se repite, o si `LIMIT` u `OFFSET` son decimales. | Revisa el orden de las cláusulas, y usa números enteros. |
| `unexpected_end_of_query` | La consulta termina antes de tiempo, a menudo después de `AND`.                                                                         | Termina la condición.                                    |
| `expected_expression`     | Falta un valor, como en `in ()`.                                                                                                        | Añade el valor o el campo.                               |
| `unclosed_parenthesis`    | Un `(` no tiene su `)`.                                                                                                                 | Añade el `)`.                                            |
| `nesting_too_deep`        | Los paréntesis están anidados demasiado.                                                                                                | Simplifica la condición.                                 |
| `invalid_duration`        | Una duración usa una unidad que PQL no tiene, como `3x`.                                                                                | Usa `m`, `h`, `d`, `w`, `mo`, `q` o `y`.                 |
| `number_out_of_range`     | Un número es demasiado grande o no es finito.                                                                                           | Usa un número más pequeño.                               |
| `query_too_long`          | La consulta tiene más de 4.000 caracteres.                                                                                              | Acórtala, o mueve una parte a una consulta guardada.     |

### Nombres y funciones

| Código                      | Qué significa                                                                            | Qué hacer                                                         |
| --------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `unknown_entity`            | El nombre después de `FROM` no es una entidad.                                           | Usa una de las nueve entidades.                                   |
| `unknown_field`             | El campo no existe en esta entidad. El mensaje puede sugerir el nombre más parecido.     | Revisa la tabla de la entidad en [Entidades](#entidades).         |
| `unknown_custom_field`      | Ningún campo personalizado de esta entidad tiene ese título.                             | Revisa el título en los ajustes de campos personalizados.         |
| `unknown_function`          | El nombre no es una función de PQL.                                                      | Revisa la tabla de [funciones](#funciones).                       |
| `not_a_function`            | Se escribió un campo como si fuera una función.                                          | Quita los paréntesis.                                             |
| `function_arity`            | La función tiene un número incorrecto de argumentos.                                     | Revisa la forma de la función en la tabla.                        |
| `invalid_argument`          | Un argumento tiene una forma incorrecta, como un desplazamiento de `startOf` sin unidad. | Usa una duración con signo, como `-1mo`.                          |
| `function_not_available`    | La función no se aplica a esta entidad, como `passRate()` en casos.                      | Usa `passRate()` en resultados o ejecuciones.                     |
| `unknown_time_unit`         | PQL no conoce la unidad de tiempo.                                                       | Usa `hour`, `day`, `week`, `month`, `quarter` o `year`.           |
| `unknown_group`             | Ningún grupo de la organización tiene ese nombre.                                        | Revisa el nombre del grupo.                                       |
| `set_function_placement`    | `membersOf()`, `activeMembers()` o `formerMembers()` se usa fuera de `in (...)`.         | Ponla dentro de `in (...)` en un campo de usuario.                |
| `unknown_relation`          | La relación no existe en esta entidad.                                                   | Revisa la tabla de [relaciones](#relaciones).                     |
| `relation_nesting_too_deep` | Las subconsultas se anidan más de dos niveles.                                           | Aplana la consulta.                                               |
| `relation_count_placement`  | `count(relación)` se usa sin comparación.                                                | Compáralo con un número, como `count(results) >= 1`.              |
| `relation_placement`        | `exists` se usa en `HAVING`.                                                             | Muévelo a `WHERE`.                                                |
| `unknown_saved_query`       | Ninguna consulta guardada que puedas ver tiene ese nombre.                               | Revisa el nombre. Solo ves las consultas a las que tienes acceso. |
| `history_not_available`     | No se guarda historial de cambios para este campo.                                       | Consulta [Campos con historial](#campos-con-historial).           |
| `history_placement`         | `was` o `changed` se usa en `HAVING`.                                                    | Muévelo a `WHERE`.                                                |

### Valores y operadores

| Código                      | Qué significa                                                               | Qué hacer                                          |
| --------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------- |
| `operator_not_allowed`      | El operador no se aplica a este campo.                                      | Revisa la tabla de [operadores](#operadores).      |
| `not_nullable`              | `is empty` se usa en un campo que siempre tiene valor.                      | Quítalo.                                           |
| `type_mismatch`             | El tipo del valor no coincide con el campo, como texto para un número.      | Usa un valor del tipo correcto.                    |
| `left_side_must_be_field`   | El lado izquierdo de una comparación no es un campo.                        | Pon el campo a la izquierda.                       |
| `not_boolean`               | Un campo se usa como condición, pero no es de sí o no.                      | Compáralo, como `archived = true`.                 |
| `unquoted_text_value`       | Un valor de texto no tiene comillas.                                        | Ponlo entre comillas.                              |
| `unknown_value`             | El valor no es una de las opciones, miembros o nombres que admite el campo. | Usa uno de los valores que indica el mensaje.      |
| `invalid_date_format`       | Una fecha no es `YYYY-MM-DD` ni `YYYY-MM-DD HH:mm`.                         | Escribe la fecha en una de esas formas.            |
| `invalid_date`              | La fecha no existe, como `2026-02-30`.                                      | Usa una fecha real.                                |
| `relative_duration_in_date` | Una duración se usa como fecha, como `7d`.                                  | Usa una función de tiempo, como `now(-7d)`.        |
| `invalid_duration_unit`     | Se usa mes, trimestre o año en un campo de duración fija.                   | Usa `m`, `h`, `d` o `w` en los campos de duración. |
| `in_list_too_long`          | Una lista `in (...)` tiene más de 200 valores.                              | Divide la lista.                                   |

### Agregados, agrupación y paginación

| Código                     | Qué significa                                                              | Qué hacer                                                |
| -------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------- |
| `aggregate_not_allowed`    | Un agregado se usa en `WHERE` o en `GROUP BY`.                             | Muévelo a `SELECT` o a `HAVING`.                         |
| `not_aggregatable`         | La función no se aplica a este campo, como `sum(title)` o `avg(passRate)`. | Usa un número, una duración o una fecha, o `passRate()`. |
| `not_groupable`            | El campo no se puede agrupar, como el texto.                               | Agrupa por otro campo, o usa `bucket()` para una fecha.  |
| `not_grouped`              | Un elemento de `SELECT` no está agrupado ni agregado.                      | Añádelo a `GROUP BY`, o envuélvelo en un agregado.       |
| `group_by_requires_select` | `GROUP BY` no tiene una lista en `SELECT`.                                 | Indica los campos agrupados y los agregados en `SELECT`. |
| `having_requires_group_by` | `HAVING` se usa sin grupo ni agregado.                                     | Añade `GROUP BY`, o un agregado en `SELECT`.             |
| `multi_valued_having`      | `HAVING` filtra una etiqueta o un campo de selección múltiple.             | Filtra en `WHERE`.                                       |
| `order_requires_group_by`  | `ORDER BY` ordena por un agregado sin grupo.                               | Añade `GROUP BY`, o un agregado en `SELECT`.             |
| `position_out_of_range`    | Una posición no corresponde a un elemento de `SELECT`.                     | Usa un número del 1 al número de elementos de `SELECT`.  |
| `not_sortable`             | El campo no se puede ordenar, como un campo de usuario o de etiquetas.     | Ordena por otro campo.                                   |
| `invalid_select_item`      | Un elemento de `SELECT` no es un campo ni un agregado.                     | Usa un campo, o un agregado como `count()`.              |
| `invalid_bucket`           | `bucket()` no tiene un campo de fecha, o no tiene unidad.                  | Usa `bucket(created, day)`.                              |
| `duplicate_alias`          | El alias ya se usa en `SELECT`.                                            | Elige otro nombre.                                       |
| `limit_out_of_range`       | `LIMIT` está fuera de 1 a 500.                                             | Usa un número del 1 al 500.                              |
| `offset_out_of_range`      | `OFFSET` es mayor que 10.000.                                              | Usa un número de hasta 10.000.                           |

Cuando el servidor todavía no admite una construcción, la consulta muestra **Todavía no disponible** en el editor, con la construcción indicada. Quita esa parte de la consulta por ahora.

## Ejemplos resueltos

Cada ejemplo responde a una pregunta frecuente. Sustituye los nombres por tus suites, hitos y entornos.

| Pregunta                                                                     | Consulta                                                                                                                                              |
| ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| ¿Qué casos fallaron en su última ejecución en staging?                       | `FROM cases WHERE lastResult(environment = "staging").status = failed ORDER BY title`                                                                 |
| ¿Qué casos críticos fallaron en su última ejecución en staging?              | `FROM cases WHERE priority = "Critical" AND lastResult(environment = "staging").status = failed ORDER BY title`                                       |
| ¿Qué defectos se reabrieron en los últimos 14 días?                          | `FROM defects WHERE status changed from resolved to open after now(-14d)`                                                                             |
| ¿Qué defectos asignados a mí se reabrieron en 30 días?                       | `FROM defects WHERE assignee = currentUser() AND status changed from resolved to open after now(-30d)`                                                |
| ¿Qué casos inestables no tienen ningún aprobado en 30 días?                  | `FROM cases WHERE isFlaky = true AND NOT exists(results where status = passed AND executed > now(-30d))`                                              |
| ¿Qué casos nunca se han ejecutado?                                           | `FROM cases WHERE NOT exists(results)`                                                                                                                |
| ¿Cuál es la tasa de aprobación por semana?                                   | `SELECT bucket(executed, week) AS period, passRate() AS rate, count() AS results FROM results GROUP BY 1 ORDER BY 1`                                  |
| ¿Cómo se reparten los resultados de este mes por estado?                     | `SELECT status, count() AS n FROM results WHERE executed >= startOf(month) GROUP BY status ORDER BY n DESC`                                           |
| ¿Cuántos defectos abiertos hay por severidad?                                | `SELECT severity, count() AS n FROM defects WHERE status != closed GROUP BY severity ORDER BY n DESC`                                                 |
| ¿Cuántas ejecuciones fallidas tuvo cada entorno el mes pasado?               | `SELECT environment, count() AS n FROM runs WHERE status = failed AND closed > startOf(month, -1mo) AND closed < startOf(month) GROUP BY environment` |
| ¿Quién ejecutó más resultados esta semana?                                   | `SELECT executedBy, count() AS n FROM results WHERE executed > now(-7d) GROUP BY executedBy ORDER BY n DESC`                                          |
| ¿Qué ejecuciones cerradas este trimestre tienen la mejor tasa de aprobación? | `FROM runs WHERE state = closed AND closed > startOf(quarter) ORDER BY passRate DESC`                                                                 |
| ¿Qué casos del árbol de Checkout cambiaron de prioridad esta semana?         | `FROM cases WHERE suite under "Checkout" AND priority changed after startOf(week)`                                                                    |
| ¿Qué casos tienen Risk en High en la suite Login?                            | `FROM cases WHERE cf["Risk"] = "High" AND suite under "Login"`                                                                                        |
| ¿Qué hitos están activos y pasaron su fecha límite?                          | `FROM milestones WHERE status = active AND due < now()`                                                                                               |

## Siguiente paso

- Escribe y ejecuta consultas en [Consultas](https://docs.probara.net/es/guides/queries/).
- Pon una consulta guardada en un dashboard en [Dashboards](https://docs.probara.net/es/guides/dashboards/#widgets-de-consulta).
- Envía consultas desde código con la [API de PQL](https://docs.probara.net/es/developers/api/pql/).
