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

Comparación de tablas temporales para PostgreSQL y Oracle GTT

Las tablas temporales son un concepto útil presente en la mayoría de los SGBD, aunque a menudo funcionan de manera diferente.

Este blog describe las características técnicas de este tipo de tablas en bases de datos PostgreSQL (versión 11) u Oracle (versión 12c) con algunos ejemplos específicos. Aunque el propósito de estas tablas podría ser el mismo para todos los SGBD, sus detalles, o la forma de implementación y manipulación, son completamente diferentes.

Esta característica podría ser utilizada tanto por desarrolladores como por administradores de bases de datos para almacenar resultados intermedios que serán necesarios para un procesamiento posterior a fin de proporcionar buenas métricas de rendimiento.

Tablas Temporales en PostgreSQL

En PostgreSQL estos objetos son válidos solo para la sesión actual:se crean, usan y sueltan en la misma sesión:la estructura de la tabla y los datos administrados solo son visibles para la sesión actual, por lo que las otras sesiones no tienen acceso a las tablas temporales creadas en las otras sesiones.

A continuación se muestra un ejemplo simple para crear una tabla temporal:

CREATE TEMPORARY TABLE tt_customer
(
     customer_id INTEGER
)
ON COMMIT DELETE ROWS;

Las tablas temporales se crean en un esquema temporal:pg_temp_nn y es posible crear índices en estas tablas:

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Como las filas de datos de estas tablas también se pueden eliminar, es posible liberar el almacenamiento ocupado mediante la ejecución de vaccum comando:

VACUUM VERBOSE tt_customer

El analizar El comando también se puede ejecutar en las tablas temporales para recopilar las estadísticas:

ANALYZE VERBOSE tt_customer;

Ambos comandos se pueden ejecutar para este tipo de tabla como comando SQL, sin embargo, el autovaccum el demonio que las ejecuta no actúa sobre las tablas temporales.

Otro punto importante a considerar está relacionado con las tablas permanentes y temporales con el mismo nombre:una vez que sucede, la tabla permanente solo se tiene en cuenta cuando se llama con su esquema como prefijo.

web_db=# BEGIN TRANSACTION;
BEGIN
web_db=# SELECT COUNT(*) FROM customers;
  count  
---------
 1030056
(1 row)

web_db=# CREATE TEMPORARY TABLE customers(
web_db(#   id INTEGER
web_db(# )
web_db-# ON COMMIT PRESERVE ROWS;
CREATE TABLE
web_db=# INSERT INTO customers(id) VALUES(1023);
INSERT 0 1
web_db=# SELECT COUNT(*) FROM customers;
 count 
-------
     1
(1 row)
web_db=# \dt *customers*
                  List of relations
  Schema   |         Name         | Type  |  Owner   
-----------+----------------------+-------+----------
 pg_temp_5 | customers            | table | postgres
 web_app   | customers            | table | postgres
 web_app   | customers_historical | table | postgres
(3 rows)
web_db=# DROP TABLE customers;
DROP TABLE
web_db=# \dt *customers*
                 List of relations
 Schema  |         Name         | Type  |  Owner   
---------+----------------------+-------+----------
 web_app | customers            | table | postgres
 web_app | customers_historical | table | postgres
(2 rows)
web_db=# SELECT COUNT(*) FROM web_app.customers; 
  count  
---------
 1030056
(1 row)
web_db=# SELECT COUNT(*) FROM customers; 
  count  
---------
 1030056
(1 row)

Del ejemplo anterior, mientras exista la tabla temporal, todas las referencias a los clientes se refiere a esta tabla en lugar de a la permanente.

Consejos para desarrolladores sobre tablas temporales

El propósito de este ejemplo es asignar una bonificación para los clientes que no hayan realizado compras o no hayan iniciado sesión durante más de un año, por lo que el script del desarrollador en lugar de usar subconsultas en consultas como una posible solución (o el uso de CTE declaración) puede usar tablas temporales (que generalmente es más rápido que usar subconsultas):

web_db=# BEGIN TRANSACTION;
BEGIN
web_db=# CREATE TEMPORARY TABLE tt_customers(
web_db(#   id INTEGER
web_db(# )
web_db-# ON COMMIT DELETE ROWS;
CREATE TABLE
web_db=# SELECT COUNT(*) FROM tt_customers;
 count 
-------
     0
(1 row)
web_db=# INSERT INTO tt_customers(id)
web_db-# SELECT customer_id
web_db-#   FROM web_app.orders
web_db-# WHERE order_dt <= NOW()-INTERVAL '6 MONTH';
INSERT 0 1030056
web_db=# SELECT COUNT(*) FROM tt_customers;
  count  
---------
 1030056
(1 row)
web_db=# DELETE FROM tt_customers c
web_db-# WHERE EXISTS(SELECT 1 
web_db(#                FROM web_app.users u JOIN web_app.login l 
web_db(#                       ON (l.user_id=u.user_id) 
web_db(#               WHERE u.customer_id=c.id 
web_db(#                 AND l.login_dt > NOW()-INTERVAL '6 MONTH'
web_db(#                 );
DELETE 194637
web_db=# SELECT COUNT(*) FROM tt_customers;
 count  
--------
 835419
(1 row)
web_db=# UPDATE web_app.customers as c SET BONUS=5
web_db-# FROM tt_customers t
web_db-# WHERE t.id = c.id;
UPDATE 835419
web_db=# SELECT COUNT(*) FROM tt_customers;
 count  
--------
 835419
(1 row)
web_db=# COMMIT TRANSACTION;
COMMIT
web_db=# SELECT COUNT(*) FROM tt_customers;
 count 
-------
     0
(1 row)

Consejos de DBA para tablas temporales

Una tarea típica de los administradores de bases de datos es purgar las tablas grandes que contienen datos que ya no se necesitan. Esto debe completarse muy rápidamente y sucede a menudo. El enfoque estándar es mover estos datos a una tabla histórica en otro esquema o a una base de datos a la que se accede con menos frecuencia.

Entonces, para realizar este movimiento, debido a problemas de rendimiento, la mejor solución podría ser usar tablas temporales:

CREATE TEMPORARY TABLE tt_customer
(
     customer_id INTEGER
)
ON COMMIT DROP;

En este ejemplo, la tabla temporal se creó con la opción DROP, lo que significa que se eliminará al final del bloque de transacciones actual.

Aquí hay otra información importante sobre las tablas temporales de PostgreSQL:

  • Las tablas temporales se eliminan automáticamente al final de una sesión o, como se presentó en el ejemplo anterior, al final de la transacción actual
  • Las tablas permanentes con el mismo nombre no son visibles para la sesión actual mientras exista la tabla temporal, a menos que se haga referencia a ellas con nombres calificados de esquema
  • Cualquier índice creado en una tabla temporal también es automáticamente temporal
  • ON COMMIT preservar filas es el comportamiento predeterminado
  • Opcionalmente, GLOBAL o LOCAL se pueden escribir antes de TEMPORARY o TEMP. Actualmente, esto no hace ninguna diferencia en PostgreSQL y está obsoleto
  • El autovacío daemon no puede acceder a estas tablas y, por lo tanto, no puede aspirar o analizar tablas temporales; sin embargo, como se mostró anteriormente, los comandos autovacuum y analysis pueden usarse como comandos SQL.

Tablas Temporales Globales (GTT) en Oracle

Este tipo de tablas se conoce en el mundo de Oracle como Tabla Temporal Global (o GTT). Estos objetos son persistentes en la base de datos y se pueden resumir por las siguientes características:

  • La estructura es estática y visible para todos los usuarios, sin embargo, su contenido solo es visible para la sesión actual
  • Se puede crear en un esquema específico (de manera predeterminada, será propiedad del usuario que emite el comando) y se construyen en el tablespace TEMP
  • Una vez creada en la base de datos no se puede volver a crear en cada sesión, sin embargo, los datos administrados por una sesión no son visibles para las demás sesiones
  • Es posible la creación de índices y generación de estadísticas
  • Como la estructura de estas tablas también está definida en la base de datos, no es posible asignar su nombre a una tabla permanente (en Oracle, dos objetos no pueden tener el mismo nombre, incluso si son de diferentes tipos)
  • No genere demasiados registros de rehacer y la sobrecarga de deshacer también es menor en comparación con una tabla permanente (solo por estas razones, el uso de GTT es más rápido) para cualquier versión anterior a 12c. A partir de la versión 12c, existe un concepto de deshacer temporal, lo que permite que se escriba un GTT en el espacio de tablas temporal, por lo que reduce las operaciones de deshacer y rehacer.

Siguiendo el mismo ejemplo presentado en PostgreSQL, la creación de un GTT es bastante similar:

CREATE GLOBAL TEMPORARY TABLE tt_customer
(
     customer_id NUMBER
)
ON COMMIT DELETE ROWS;

Es posible también la creación de índices.

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Antes de Oracle 12c, la generación de estadísticas para tablas temporales globales tenía un comportamiento global:las estadísticas generadas en una sesión específica para un GTT específico eran visibles y utilizadas para las demás sesiones (¡solo estadísticas, no los datos!), sin embargo, a partir de la versión 12c es posible que cada sesión genere sus propias estadísticas.

En primer lugar, es necesario establecer la preferencia global_temp_table_stats a sesión :

exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);

y luego la generación de estadísticas:

exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);

La tabla temporal global existente podría verificarse mediante la ejecución de la siguiente consulta:

select table_name from all_tables where temporary = 'Y';

Consejos para desarrolladores sobre tablas temporales globales (GTT)

Siguiendo el ejemplo de la sección de PostgreSQL:para asignar un bono a los clientes que no hayan realizado compras o no hayan iniciado sesión durante más de un año, el uso de tablas temporales globales en Oracle tiene el mismo objetivo que en PostgreSQL:lograr un mejor rendimiento ya sea en el uso de recursos o en la velocidad de ejecución.

SQL> SELECT COUNT(*) FROM tt_customers;
  COUNT(*)
----------
         0
SQL>
SQL> INSERT INTO tt_customers(id)
  2  SELECT customer_id
  3    FROM orders
  4  WHERE order_dt <= ADD_MONTHS(SYSDATE,-6);
1030056 rows created.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
  COUNT(*)
----------
   1030056
SQL>
SQL> DELETE FROM tt_customers c
  2  WHERE EXISTS(SELECT 1
  3                 FROM users u JOIN login l
  4                        ON (l.user_id=u.user_id)
  5                WHERE u.customer_id=c.id
  6                  AND l.login_dt > ADD_MONTHS(SYSDATE,-6)
  7                  );
194637 rows deleted.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
  COUNT(*)
----------
    835419
SQL>
SQL> UPDATE CUSTOMERS c SET BONUS=5
  2  WHERE EXISTS(SELECT 1 FROM tt_customers tc WHERE tc.id=c.id);
835419 rows updated.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
  COUNT(*)
----------
    835419
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
  COUNT(*)
----------
         0

SQL>

De forma predeterminada en Oracle, un bloque/declaración SQL/PLSQL inicia implícitamente una transacción.

Consejos de DBA para tablas temporales globales (GTT)

Como la declaración soltar no existe para tablas temporales globales el comando para crear la tabla es el mismo que el anterior:

CREATE GLOBAL TEMPORARY TABLE tt_customer
(
     customer_id NUMBER
)
ON COMMIT DELETE ROWS;

El fragmento de código equivalente en Oracle para purgar el cliente tabla es la siguiente:

SQL> INSERT INTO tt_customers(id)
  2  SELECT l.user_id
  3    FROM users u JOIN login l
  4           ON (l.user_id=u.user_id)
  5   WHERE l.login_dt < ADD_MONTHS(SYSDATE,-12);
194637 rows created.
SQL>
SQL> INSERT INTO tt_customers(id)
  2  SELECT user_id
  3    FROM web_deactive;
2143 rows created.
SQL>
SQL> INSERT INTO tt_customers(id)
  2  SELECT user_id
  3    FROM web_black_list;
4234 rows created.
SQL>
SQL> INSERT INTO customers_historical(id,name)
  2  SELECT c.id,c.name
  3  FROM customers c,
  4  tt_customers tc
  5  WHERE tc.id = c.id;
201014 rows created.
SQL>
SQL> DELETE FROM customers c
  2  WHERE EXISTS (SELECT 1 FROM  tt_customers tc WHERE tc.id = c.id );
201014 rows deleted.

La biblioteca pg_global_temp_tables

Como se mencionó anteriormente, las tablas temporales en PostgreSQL no se pueden invocar usando la notación schema.table , por lo que la biblioteca pg_global_temp_tables (hay algunas bibliotecas similares disponibles en github) es una solución muy útil para usar en migraciones de bases de datos de Oracle a PostgreSQL.

Para mantener la notación Oracle schema.temporary_table en consultas o procedimientos almacenados:

SELECT c.id,c.nam
    FROM web_app.tt_customers tc,
                 Web_app.customers c
    WHERE c.id = tc.id

Permite permanecer las tablas temporales sobre el código con la notación de esquema.

Básicamente, consiste en una vista:web_app.tt_customers creado bajo el esquema en el que se supone que tiene la tabla temporal y esta vista consultará la tabla temporal tt_customers a través de una función llamada web_app.select_tt_customers :

CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS 
  SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();

Esta función devuelve el contenido de la tabla temporal:

CREATE OR REPLACE FUNCTION WEB_APP.SELECT_TT_CUSTOMERS() RETURNS TABLE(ID INR, NAME VARCHAR) AS $$
BEGIN
    CREATE TEMPORARY TABLE IF NOT EXISTS TT_CUSTOMERS(ID INT, NAME) ON COMMIT DROP;
    RETURN QUERY SELECT * FROM TT_CUSTOMERS;
END;
$$ LANGUAGE PLPGSQL;  

Resumen

Las tablas temporales se utilizan esencialmente para almacenar resultados intermedios y así evitar una computación compleja y pesada,

A continuación, se enumeran algunas características de las tablas temporales, ya sea en PostgreSQL u Oracle:

  • Se puede usar a la vista
  • Puede usar el comando TRUNCATE
  • No se puede particionar
  • La restricción de clave externa en tablas temporales no está permitida
  • Este tipo de tablas son una alternativa a las CTE (Common Table Expressions) también conocidas por los profesionales de Oracle como cláusula WITH
  • En términos de seguridad y privacidad, estas tablas son un activo valioso porque los datos solo son visibles para una sesión actual
  • Las tablas temporales se eliminan automáticamente (en PostgreSQL) o se eliminan (en Oracle) una vez que finaliza la sesión/transacción.

Para las tablas temporales en PostgreSQL es recomendable no usar el mismo nombre de una tabla permanente en una tabla temporal. Por parte de Oracle, es una buena práctica la generación de estadísticas para las sesiones que incluyen un volumen considerable de datos en GTT para obligar al Optimizador basado en costos (CBO) a elegir el mejor plan para las consultas que utilizan este tipo de tablas. .