sql >> Base de Datos >  >> RDS >> PostgreSQL

Consulta de tabulación cruzada de PostgreSQL

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 valor 7 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 generar text , date , numeric etc. para los atributos respectivos. Hay un código de ejemplo al final del capítulo crosstab(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?