PostgreSQL: colonne generate


16

PostgreSQL supporta le colonne generate ? Conosciute anche come colonne virtuali . Sto Non parlando di IDENTITYcolonne .

Non riesco a trovare alcuna informazione su questa straordinaria funzionalità ma so che è disponibile su SQL Server e nelle ultime versioni di MariaDB e MySQL.

La funzionalità è menzionata nello standard SQL: 2003 e ci sono state discussioni sui forum PostgreSQL intorno al 2006, ma non riesco a trovare nulla di sostanziale in merito.

C'è qualche discussione su SO, ma ora è piuttosto vecchio, quindi potrebbe non essere aggiornato.


2
Questa risposta correlata dal 2012 su SO può essere di aiuto: stackoverflow.com/questions/11165450/… Ancora valido.
Erwin Brandstetter,

@ErwinBrandstetter Spiacente, ho perso questo commento. È un trucco utile. Grazie.
Manngo,

Risposte:


17

Non sono sicuro se questo è ciò che vuoi, ma la notazione degli attributi row.full_namee la funzione full_name(row)sono equivalenti in postgresql.

Ciò significa che prendi un tavolo

CREATE TABLE people (
  first_name text,
  last_name text
);

e una funzione:

CREATE FUNCTION full_name(people) RETURNS text AS $$
  SELECT $1.first_name || ' ' || $1.last_name;
$$ LANGUAGE SQL;

e chiamalo così:

select full_name from people

È quello che ti serve?

Per velocizzare le cose puoi creare un indice di espressione:

CREATE INDEX people_full_name_idx ON people
USING GIN (to_tsvector('english', full_name(people)));

O memorizza tutto in una vista materializzata.

Esempio preso da qui: http://bernardoamc.github.io/sql/2015/05/11/postgres-virtual-columns/


2
Questa è la risposta corretta Vedi, ad esempio, come Postgrest si riferisce a questo comportamento come "colonne calcolate".
fiatjaf,

Errore di battitura, penso - la selezione dovrebbe essere select people.full_name from peopleo select full_name(people) from people?
Barguast il

No, funziona così. Il prefisso in "selezionare people.full_name dalle persone" può essere lasciato fuori come nel normale SQL.
Fabian Zeindl,

Ho perso questa risposta, arrivando, per così dire, molto tempo dopo che avevo rinunciato. Grazie per il suggerimento
Manngo,

1
Potresti cambiare la risposta accettata allora?
Fabian Zeindl,

6

No, attualmente non è supportato (a partire da Postgres 9.6).

L'unica soluzione è utilizzare un trigger o una vista se si tratta di un semplice calcolo che non è necessario indicizzare.


Ratti. Suppongo che potrei andare per una visione materializzata se ho bisogno della performance. Ho aggiunto una richiesta per la funzione, poiché è già disponibile in concorso.
Manngo,

1
Non è necessario un MVIEW. Una colonna con un trigger ti consentirà anche di indicizzare il contenuto della colonna
a_horse_with_no_name

Ho un problema filosofico con la memorizzazione di colonne reali aggiuntive che sono fondamentalmente una ripetizione degli altri dati. De-normalizza il tavolo.
Manngo,

5
Bene, una colonna calcolata è esattamente questo: la memorizzazione di dati non normalizzati. Non importa come viene generato il valore della colonna calcolata. Non vedo una differenza concettuale tra una colonna calcolata "reale" e una generata da un trigger
a_horse_with_no_name

Un'altra soluzione alternativa (per alcuni casi) è l'indicizzazione di un'espressione.
ypercubeᵀᴹ

5

Sì: GENERATED ALWAYS AS … STORED

Postgres 12 aggiunge la funzionalità per le colonne generate, come indicato nello standard SQL: 2003 .

Il valore viene generato al momento di un INSERTo UPDATE, quindi memorizzato con la riga come qualsiasi altro valore.

Un generato deve essere basato su una colonna di base della stessa tabella o su una funzione immutabile .

La sintassi è semplice, una clausola su CREATE TABLE:

GENERATED ALWAYS AS ( generation_expr ) STORED 

Esempio:

CREATE TABLE people (
    ...,
    height_cm NUMERIC,
    height_in NUMERIC GENERATED ALWAYS AS ( height_cm / 2.54 ) STORED
);

Caratteristiche:

  • Può essere indicizzato.
  • Parte dello standard SQL.

Avvertenze:

  • Basato su colonne della stessa tabella (tabelle non correlate)
  • Non consentito per il partizionamento (non può far parte di una chiave di partizione)
  • Dati sempre scritti in riga, occupando spazio in memoria
    • Le funzionalità future potrebbero offrire VIRTUAL per i valori calcolati al volo senza memorizzazione
  • Profondità a generazione singola (usa la colonna di base, non un'altra colonna generata)
  • Non esiste GENERATED BY DEFAULT (non è possibile ignorare il valore)
  • Impossibile accedere al gen-col nel trigger PRIMA (valore non ancora determinato)
  • Le funzioni devono essere immutabili

Vedere:


Grazie per quelle informazioni. Vedo che la versione 12 non è ancora stata completamente rilasciata, ma non vedo l'ora. Noto che PostgreSQL utilizza la sintassi più standard, ma è altrimenti uguale a MSSQL. Ho trovato le specifiche SQL2003 qui: sigmodrecord.org/publications/sigmodRecord/0403/… . Ho sempre detto che SQL è uno standard molto lento e le implementazioni DBMS sono persino lente.
Manngo,

0

A seconda del caso d'uso, è possibile ottenere questo tipo di comportamento dichiarando una nuova colonna e popolandola con un trigger su insert / update.

Vorrei utilizzare le risposte di cui sopra, se possibile, per evitare la duplicazione dei dati che potrebbero essere derivati ​​da ciò che già avete, ma fa il trucco e potrebbe essere utile per i campi derivati ​​ad alta intensità computazionale che volete calcolare una volta e salvare.

Ho considerato questo approccio per affrontare un problema in cui a volte avevo solo 15 cifre di una chiave di 18 cifre (le ultime 3 cifre sono solo una somma di controllo) ma volevo essere in grado di far valere una relazione di chiave esterna.

Documenti PG sui trigger: https://www.postgresql.org/docs/9.6/sql-createtrigger.html

Esempio W3: https://www.w3resource.com/PostgreSQL/postgresql-triggers.php

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.