sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL 9.3:dynamische draaitabel

U kunt dit doen met crosstab() uit de extra module tablefunc:

SELECT b
     , COALESCE(a1, 0) AS "A1"
     , COALESCE(a2, 0) AS "A2"
     , COALESCE(a3, 0) AS "A3"
     , ... -- all the way up to "A30"
FROM   crosstab(
         'SELECT colb, cola, 1 AS val FROM matrix
          ORDER  BY 1,2'
        , $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
       ) AS t (b text
             , a1  int, a2  int, a3  int, a4  int, a5  int, a6  int
             , a7  int, a8  int, a9  int, a10 int, a11 int, a12 int
             , a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
             , a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
             , a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);

Als NULL in plaats van 0 werkt ook, het kan gewoon SELECT * . zijn in de buitenste vraag.
Gedetailleerde uitleg:

  • PostgreSQL-kruistabelquery

De speciale "moeilijkheid" hier:geen werkelijke "waarde". Dus voeg 1 AS val . toe als laatste kolom.

Onbekend aantal categorieën

Een volledig dynamische zoekopdracht (met onbekend resultaattype) is niet mogelijk in een enkele zoekopdracht. Je hebt twee nodig vragen. Bouw eerst dynamisch een instructie zoals hierboven en voer deze vervolgens uit. Details:

  • Meerdere max()-waarden selecteren met een enkele SQL-instructie

  • PostgreSQL kolommen naar rijen converteren? Transponeren?

  • Dynamisch kolommen genereren voor kruistabel in PostgreSQL

  • Dynamisch alternatief voor draaien met CASE en GROUP BY

Te veel categorieën

Als u het maximale aantal kolommen (1600) overschrijdt, is een klassieke kruistabel onmogelijk, omdat het resultaat niet kan worden weergegeven met afzonderlijke kolommen. (Ook zouden menselijke ogen nauwelijks een tabel met zoveel kolommen kunnen lezen)

Arrays of documenttypes zoals hstore of jsonb zijn het alternatief. Hier is een oplossing met arrays:

SELECT colb, array_agg(cola) AS colas
FROM  (
   SELECT colb, right(colb, -1)::int AS sortb
        , CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
   FROM        (SELECT DISTINCT colb FROM matrix) b
   CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
   LEFT   JOIN matrix m USING (colb, cola)
   ORDER  BY sortb, right(cola, -1)::int 
   ) sub
GROUP  BY 1, sortb
ORDER  BY sortb;
  • Bouw het volledige waardenraster met:

                (SELECT DISTINCT colb FROM matrix) b
    CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
    
  • LEFT JOIN bestaande combinaties, sorteer op het numerieke deel van de naam en aggregeer in arrays.

    • right(colb, -1)::int trimt het hoofdteken van 'A3' en giet de cijfers naar integer zodat we een juiste sorteervolgorde krijgen.

Basismatrix

Als je alleen een tabel van 0 . wilt een 1 waarbij x = y , dit kan goedkoper:

SELECT x, array_agg((x = y)::int) AS y_arr
FROM   generate_series(1,10) x
     , generate_series(1,10) y
GROUP  BY 1
ORDER  BY 1;

SQL Fiddle voortbouwend op degene die je hebt opgegeven in de opmerkingen.

Merk op dat sqlfiddle.com momenteel een bug heeft die de weergave van arraywaarden vernietigt. Dus cast ik naar text daar omheen te werken.




  1. Hoe Oracle en Kafka . te integreren

  2. Maak verbinding met mysql op Amazon EC2 vanaf een externe server

  3. MySQL-server beveiligen

  4. Een opgeslagen procedure uitvoeren in een andere opgeslagen procedure in SQL-server