Instale el módulo adicional tablefunc
una vez por base de datos, que proporciona la función crosstab()
. Desde Postgres 9.1 puedes usar CREATE EXTENSION
por eso:
CREATE EXTENSION IF NOT EXISTS tablefunc;
Caso de prueba mejorado
CREATE TABLE tbl (
section text
, status text
, ct integer -- "count" is a reserved word in standard SQL
);
INSERT INTO tbl VALUES
('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
, ('C', 'Inactive', 7); -- ('C', 'Active') is missing
Formulario simple:no apto para atributos faltantes
crosstab(text)
con 1 parámetro de entrada:
SELECT *
FROM crosstab(
'SELECT section, status, ct
FROM tbl
ORDER BY 1,2' -- needs to be "ORDER BY 1,2" here
) AS ct ("Section" text, "Active" int, "Inactive" int);
Devoluciones:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | 7 | -- !!
- No es necesario enviar ni renombrar.
- Tenga en cuenta lo incorrecto resultado para
C
:el valor7
se rellena para la primera columna. A veces, este comportamiento es deseable, pero no para este caso de uso. - La forma simple también está limitada a exactamente tres columnas en la consulta de entrada proporcionada:row_name , categoría , valor . No hay espacio para columnas adicionales como en la alternativa de 2 parámetros a continuación.
Forma segura
crosstab(text, text)
con 2 parámetros de entrada:
SELECT *
FROM crosstab(
'SELECT section, status, ct
FROM tbl
ORDER BY 1,2' -- could also just be "ORDER BY 1" here
, $$VALUES ('Active'::text), ('Inactive')$$
) AS ct ("Section" text, "Active" int, "Inactive" int);
Devoluciones:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | | 7 -- !!
-
Tenga en cuenta el resultado correcto para
C
. -
El segundo parámetro puede ser cualquier consulta que devuelva una fila por atributo que coincida con el orden de la definición de columna al final. A menudo querrá consultar distintos atributos de la tabla subyacente como esta:
'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
Eso está en el manual.
Ya que tiene que deletrear todas las columnas en una lista de definición de columna de todos modos (excepto para crosstabN()
variantes), normalmente es más eficiente proporcionar una lista corta en un VALUES
expresión como la demostrada:
$$VALUES ('Active'::text), ('Inactive')$$)
O (no en el manual):
$$SELECT unnest('{Active,Inactive}'::text[])$$ -- short syntax for long lists
-
Utilicé cotización en dólares para facilitar la cotización.
-
Incluso puede generar columnas con diferentes tipos de datos con
crosstab(text, text)
- siempre que la representación de texto de la columna de valor sea una entrada válida para el tipo de destino. De esta manera, puede tener atributos de diferente tipo y generartext
,date
,numeric
etc. para los atributos respectivos. Hay un código de ejemplo al final del capítulocrosstab(text, text)
en el manual.
db<>violín aquí
Efecto del exceso de filas de entrada
El exceso de filas de entrada se maneja de manera diferente:filas duplicadas para la misma combinación ("row_name", "category") - (section, status)
en el ejemplo anterior.
El 1 parámetro El formulario rellena las columnas de valores disponibles de izquierda a derecha. Los valores en exceso se descartan.
Las filas de entrada anteriores ganan.
El 2 parámetros El formulario asigna cada valor de entrada a su columna dedicada, sobrescribiendo cualquier asignación anterior.
Las filas de entrada posteriores ganan.
Por lo general, no tiene duplicados para empezar. Pero si lo hace, ajuste cuidadosamente el orden de clasificación según sus requisitos y documente lo que sucede.
O obtenga resultados rápidos y arbitrarios si no le importa. Solo sé consciente del efecto.
Ejemplos avanzados
-
Pivote en múltiples columnas usando Tablefunc - también demostrando las "columnas adicionales" mencionadas
-
Alternativa dinámica al pivote con CASE y GROUP BY
\crosstabview
en psql
PostgreSQL 9.6 agregó este metacomando a su terminal interactivo predeterminado psql. Puede ejecutar la consulta que usaría como primer crosstab()
y aliméntelo a \crosstabview
(inmediatamente o en el siguiente paso). Me gusta:
db=> SELECT section, status, ct FROM tbl \crosstabview
Resultado similar al anterior, pero es una característica de representación en el lado del cliente exclusivamente. Las filas de entrada se tratan de forma ligeramente diferente, por lo tanto, ORDER BY
no es requerido. Detalles de \crosstabview
en el manual Hay más ejemplos de código en la parte inferior de esa página.
Respuesta relacionada en dba.SE por Daniel Vérité (el autor de la función psql):
- ¿Cómo genero un CROSS JOIN pivotado donde se desconoce la definición de la tabla resultante?