Postgres: ¿Cómo es SET NOT NULL "más eficiente" que la restricción CHECK


17

En documentos de PostgreSQL para restricciones , dice

Una restricción no nula es funcionalmente equivalente a crear una restricción de verificación CHECK (column_name IS NOT NULL), pero en PostgreSQL crear una restricción explícita no nula es más eficiente.

me pregunto

  • ¿Qué significa exactamente "más eficiente"?
  • ¿Cuáles son las desventajas de usar en CHECK (column_name IS NOT NULL)lugar de SET NOT NULL?

Quiero poder agregar una NOT VALID CHECKrestricción y validarla por separado (por lo tanto, AccessExclusiveLocksolo se mantiene durante un corto período de tiempo para agregar la restricción y luego ShareUpdateExclusiveLockse mantiene para el paso de validación más largo):

ALTER TABLE table_name
  ADD CONSTRAINT column_constraint
  CHECK (column_name IS NOT NULL)
  NOT VALID;
ALTER TABLE table_name
  VALIDATE CONSTRAINT column_constraint;

En lugar de:

ALTER TABLE table_name
  ALTER COLUMN column_name
  SET NOT NULL;


¿Cómo son los planes de ejecución si haces un not incon ambas variantes? ¿Son iguales o difieren?
Martin Smith

Respuestas:


12

Mi conjetura: "más eficiente" significa "se requiere menos tiempo para realizar la verificación" (ventaja de tiempo). También puede significar "se requiere menos memoria para realizar la verificación" (ventaja de espacio). También podría significar "tiene menos efectos secundarios" (como no bloquear algo o bloquearlo por períodos más cortos de tiempo) ... pero no tengo forma de saber o verificar esa "ventaja adicional".

No puedo pensar en una manera fácil de verificar una posible ventaja de espacio (que, supongo, no es tan importante cuando la memoria hoy en día es barata). Por otro lado, no es tan difícil verificar la posible ventaja de tiempo: solo cree dos tablas que sean iguales, con la única excepción de la restricción. Inserte un número suficientemente grande de filas, repita varias veces y verifique los tiempos.

Esta es la configuración de la tabla:

CREATE TABLE t1
(
   id serial PRIMARY KEY, 
   value integer NOT NULL
) ;

CREATE TABLE t2
(
  id serial PRIMARY KEY,
  value integer
) ;

ALTER TABLE t2
  ADD CONSTRAINT explicit_check_not_null
  CHECK (value IS NOT NULL);

Esta es una tabla adicional, utilizada para almacenar tiempos:

CREATE TABLE timings
(
   test_number integer, 
   table_tested integer /* 1 or 2 */, 
   start_time timestamp without time zone,
   end_time timestamp without time zone,
   PRIMARY KEY(test_number, table_tested)
) ;

Y esta es la prueba realizada, usando pgAdmin III, y la función pgScript .

declare @trial_number;
set @trial_number = 0;

BEGIN TRANSACTION;
while @trial_number <= 100
begin
    -- TEST FOR TABLE t1
    -- Insert start time
    INSERT INTO timings(test_number, table_tested, start_time) 
    VALUES (@trial_number, 1, clock_timestamp());

    -- Do the trial
    INSERT INTO t1(value) 
    SELECT 1.0
      FROM generate_series(1, 200000) ;

    -- Insert end time
    UPDATE timings 
       SET end_time=clock_timestamp() 
     WHERE test_number=@trial_number and table_tested = 1;

    -- TEST FOR TABLE t2
    -- Insert start time
    INSERT INTO timings(test_number, table_tested, start_time) 
    VALUES (@trial_number, 2, clock_timestamp());

        -- Do the trial
    INSERT INTO t2(value) 
    SELECT 1.0
    FROM generate_series(1, 200000) ;

    -- Insert end time
    UPDATE timings 
       SET end_time=clock_timestamp() 
     WHERE test_number=@trial_number and table_tested = 2;

    -- Increase loop counter
    set @trial_number = @trial_number + 1;
end 
COMMIT TRANSACTION;

El resultado se resume en la siguiente consulta:

SELECT
    table_tested, 
    sum(delta_time), 
    avg(delta_time), 
    min(delta_time), 
    max(delta_time), 
    stddev_pop(delta_time) 
FROM
    (
    SELECT
        table_tested, extract(epoch from (end_time - start_time)) AS delta_time
    FROM
        timings
    ) AS delta_times
GROUP BY
    table_tested 
ORDER BY
    table_tested ;

Con los siguientes resultados:

table_tested | sum     | min   | max   | avg   | stddev_pop
-------------+---------+-------+-------+-------+-----------
           1 | 176.740 | 1.592 | 2.280 | 1.767 | 0.08913
           2 | 177.548 | 1.593 | 2.289 | 1.775 | 0.09159

Un gráfico de los valores muestra una variabilidad importante:

Tiempo empleado por cada inserción de 200000 filas (en segundos)

Entonces, en la práctica, el CHEQUE (columna NO ES NULO) es un poco más lento (en un 0.5%). Sin embargo, esta pequeña diferencia puede deberse a cualquier razón aleatoria, siempre que la variabilidad de los tiempos sea mucho mayor que eso. Entonces, no es estadísticamente significativo.

Desde un punto de vista práctico, ignoraría mucho el "más eficiente" NOT NULL, porque realmente no veo que sea significativo; Considerando que creo que la ausencia de un AccessExclusiveLockes una ventaja.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.