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

Nuevo en PostgreSQL 12:Columnas generadas

PostgreSQL 12 viene con una nueva característica llamada columnas generadas . Otros RDBMS populares ya admiten columnas generadas como "columnas calculadas" o "columnas virtuales". Con Postgres 12, ahora también puede usarlo en PostgreSQL. Siga leyendo para obtener más información.

¿Qué es una columna generada?

Una columna generada es algo así como una vista, pero para columnas. He aquí un ejemplo básico:

db=# CREATE TABLE t (w real, h real, area real GENERATED ALWAYS AS (w*h) STORED);
CREATE TABLE
db=# INSERT INTO t (w, h) VALUES (10, 20);
INSERT 0 1
db=# SELECT * FROM t;
 w  | h  | area
----+----+------
 10 | 20 |  200
(1 row)

db=#

Creamos una tabla t con dos columnas regulares llamadas w y h y una columna generada llamada área . El valor del área se calcula en el momento de la creación de la fila y se conserva en el disco.

El valor de las columnas generadas se vuelve a calcular cuando se actualiza la fila:

db=# UPDATE t SET w=40;
UPDATE 1
db=# SELECT * FROM t;
 w  | h  | area
----+----+------
 40 | 20 |  800
(1 row)

db=#

Anteriormente, dicha funcionalidad generalmente se lograba con disparadores, pero con las columnas generadas esto se vuelve mucho más elegante y limpio.

Algunos puntos que debe saber sobre las columnas generadas:

  • Persistencia :actualmente, el valor de las columnas generadas debe persistir y no se puede calcular sobre la marcha en el momento de la consulta. La palabra clave "ALMACENADO" debe estar presente en la definición de la columna.
  • La expresión :La expresión utilizada para calcular el valor tiene que ser inmutable , es decir, tiene que ser determinista. Puede depender de otras columnas, pero no de otras columnas generadas, de la tabla.
  • Índices :las columnas generadas se pueden usar en índices, pero no se pueden usar como clave de partición para tablas particionadas.
  • Copiar y pg_dump :Los valores de las columnas generadas se omiten en la salida de los comandos "pg_dump" y "COPY table", ya que no es necesario. Puede incluirlos explícitamente en COPY usando COPY (SELECT * FROM t) TO STDOUT en lugar de COPY t TO STDOUT .

Un Ejemplo Práctico

Agreguemos soporte de búsqueda de texto completo a una tabla usando columnas generadas. Aquí hay una tabla que almacena el texto completo de todas las obras de Shakespeare:

CREATE TABLE scenes (
    workid text,       -- denotes the name of the play (like "macbeth")
    act integer,       -- the act (like 1)
    scene integer,     -- the scene within the act (like 7)
    description text,  -- short desc of the scene (like "Macbeth's castle.")
    body text          -- full text of the scene
);

Así es como se ven los datos:

shakespeare=# SELECT workid, act, scene, description, left(body, 200) AS body_start
shakespeare-# FROM scenes WHERE workid='macbeth' AND act=1 AND scene=1;
 workid  | act | scene |   description   |                  body_start
---------+-----+-------+-----------------+----------------------------------------------
 macbeth |   1 |     1 | A desert place. | [Thunder and lightning. Enter three Witches]+
         |     |       |                 |                                             +
         |     |       |                 | First Witch: When shall we three meet again +
         |     |       |                 | In thunder, lightning, or in rain?          +
         |     |       |                 |                                             +
         |     |       |                 | Second Witch: When the hurlyburly's done,   +
         |     |       |                 | When the battle's lost and won.             +
         |     |       |                 |
(1 row)

Añadiremos una columna que contendrá los lexemas en el valor de “cuerpo”. La función to_tsvector devuelve los lexemas que necesitamos:

shakespeare=# SELECT to_tsvector('english', 'move moving moved movable mover movability');
             to_tsvector
-------------------------------------
 'movabl':4,6 'move':1,2,3 'mover':5
(1 row)

El tipo del valor devuelto por to_tsvector es tsvector.

Modifiquemos la tabla para agregar una columna generada:

ALTER TABLE scenes
  ADD tsv tsvector
    GENERATED ALWAYS AS (to_tsvector('english', body)) STORED;

Puedes ver el cambio con \d :

shakespeare=# \d scenes
                                                Table "public.scenes"
   Column    |   Type   | Collation | Nullable |                               Default
-------------+----------+-----------+----------+----------------------------------------------------------------------
 workid      | text     |           | not null |
 act         | integer  |           | not null |
 scene       | integer  |           | not null |
 description | text     |           |          |
 body        | text     |           |          |
 tsv         | tsvector |           |          | generated always as (to_tsvector('english'::regconfig, body)) stored
Indexes:
    "scenes_pkey" PRIMARY KEY, btree (workid, act, scene)

Y así, ahora puedes hacer búsquedas de texto completo:

shakespeare=# SELECT
  workid, act, scene, ts_headline(body, q)
FROM (
  SELECT
    workid, act, scene, body, ts_rank(tsv, q) as rank, q
  FROM
    scenes, plainto_tsquery('uneasy head') q
  WHERE
    tsv @@ q
  ORDER BY
    rank DESC
  LIMIT
    5
) p
ORDER BY
  rank DESC;
  workid  | act | scene |                        ts_headline
----------+-----+-------+-----------------------------------------------------------
 henry4p2 |   3 |     1 | <b>Uneasy</b> lies the <b>head</b> that wears a crown.   +
          |     |       |                                                          +
          |     |       |    Enter WARWICK and Surrey                              +
          |     |       |                                                          +
          |     |       | Earl of Warwick
 henry5   |   2 |     2 | <b>head</b> assembled them?                              +
          |     |       |                                                          +
          |     |       | Lord Scroop: No doubt, my liege, if each man do his best.+
          |     |       |                                                          +
          |     |       | Henry V: I doubt not that; since we are well persuaded   +
          |     |       | We carry not a heart with us from hence
(2 rows)

shakespeare=#

Leer más

Si necesita datos precalculados/"almacenados en caché", especialmente con una carga de trabajo de pocas escrituras y muchas lecturas, las columnas generadas deberían ayudar a simplificar mucho su aplicación/código del lado del servidor.

Puede leer la documentación v12 de CREATE TABLE y ALTER TABLE para ver la sintaxis actualizada.