Pyxis Logo
Inicio / Formación IBM / Artículo técnico

Lab práctica de compresión de tablas e índices en Db2

Cómo estimar, activar y validar compresión de datos en tablas row-organized y sus índices.

24 min de lectura
Publicado 2026-05-13
Pyxis editorial team
Califique este artículo
Calificación media: Sin calificación
Su calificación: Sin calificación
visualizaciones: 0
Ilustración técnica de compresión de tablas e índices en Db2

Esta lab está dedicada a la compresión de datos de tablas e índices en Db2. El foco está en tablas con organización por filas, ilustrando decisiones prácticas entre COMPRESS YES STATIC, COMPRESS YES ADAPTIVE, VALUE COMPRESSION y compresión de índices.

1. Objetivos de la lab

Al final deberías poder:

  • Distinguir las opciones actuales de compresión para tablas row-organized en Db2
  • Estimar ganancias en tablas sin compresión activa
  • Estimar ganancias adicionales en tablas que ya están comprimidas
  • Estimar ganancias potenciales para índices sin compresión
  • Activar compresión para tablas e índices
  • Reorganizar los objetos para materializar la compresión

2. Software necesario y cómo obtenerlo

Para esta lab usa preferentemente:

  • Db2 LUW 12.1 o 11.5.x en Linux. Community Edition es suficiente.
  • Una cuenta con privilegios de propietario de la instancia

Puedes obtener IBM Db2 a través del canal oficial de IBM para descargas del producto.

3. Opciones actuales de compresión en tablas con organización por filas

Cuando la tabla está organizada por filas, las opciones relevantes son las siguientes:

OpciónCláusula típicaUso principal
Classic row compressionCOMPRESS YES STATICDiccionario estable, útil cuando quieres una compresión predecible tras REORG
Adaptive compressionCOMPRESS YES ADAPTIVEDiccionario adaptativo, normalmente la opción más interesante para tablas OLTP con evolución continua de datos
Value compressionACTIVATE VALUE COMPRESSIONElimina almacenamiento redundante de NULL, cadenas vacías y valores repetidos en columnas específicas
System default compressionALTER col COMPRESS SYSTEM DEFAULTEvita almacenar explícitamente valores default del sistema, como ceros o blancos
Index compressionALTER INDEX ... COMPRESS YESReduce el espacio físico de los índices, especialmente en entornos OLTP y DW con muchos prefijos repetidos

En la práctica:

  • La compresión estática y la adaptativa no pueden usarse simultáneamente en la misma tabla
  • Value compression puede combinarse con uno de los dos modos de row compression
  • La compresión de índices se controla por separado

Qué distingue STATIC de ADAPTIVE

En ambos casos, Db2 reduce el espacio ocupado por las filas sustituyendo patrones de datos repetidos por símbolos que ocupan menos espacio. La diferencia real está en dónde se establece el diccionario de compresión y con qué frecuencia puede adaptarse al patrón real de los datos.

COMPRESS YES STATIC

En la compresión estática, Db2 usa solo un diccionario global. Ese diccionario intenta capturar los patrones más repetidos encontrados en una muestra representativa de toda la tabla.

  • El diccionario global puede crearse automáticamente cuando se alcanza un cierto umbral de datos insertados, o puede crearse mediante REORG.
  • Una vez creado, ese diccionario no se ajusta continuamente a los patrones locales de datos de cada página

STATIC suele funcionar bien cuando:

  • La tabla tiene patrones de fila muy repetitivos
  • Los valores más comunes cambian poco con el tiempo

COMPRESS YES ADAPTIVE

En la compresión adaptativa, Db2 usa dos capas de compresión:

  • El mismo diccionario global usado en la compresión estática
  • Diccionarios adicionales a nivel de páginas específicas, que permiten comprimir de forma eficaz patrones de repetición locales

Db2 puede crear dinámicamente diccionarios de compresión a nivel de página, basándose en los patrones repetidos existentes en esa página. Si es necesario, ese diccionario local puede recrearse automáticamente más tarde.

El diccionario global sigue existiendo y sigue comprimiendo patrones globales. La ganancia adicional del modo adaptativo viene de una segunda capa de compresión, que captura la repetición local que el diccionario global no representa tan bien y sustituye los valores repetidos localmente por símbolos mapeados mediante un diccionario de página.

La compresión adaptativa suele ser una buena apuesta cuando:

  • Existen muchas columnas opcionales
  • Hay cadenas de texto con repetición parcial, pero no totalmente uniforme a lo largo de la tabla
  • El perfil de los datos cambia con el tiempo, por ejemplo por canales, campañas, estados operativos o nuevos tipos de carga

Imagina una tabla de pedidos como la de esta lab:

  • Algumas páginas ficam dominadas por encomendas ONLINE
  • Outras acumulam mais encomendas PARTNER
  • Certos blocos têm mais remarks, promo_code e gift_message
  • Outros têm combinações diferentes de shipping_mode, sales_rep ou approval_code

Esta lab no establece la compresión adaptativa como primera elección. Primero mide:

  • PCTPAGESSAVED_STATIC
  • PCTPAGESSAVED_ADAPTIVE

y solo después decide. Si la diferencia es irrelevante, el diccionario global ya está haciendo la mayor parte del trabajo y la compresión estática puede ser suficiente. Si la diferencia es significativa, eso indica que la compresión adaptativa puede explotar repetición local adicional para obtener una compresión más eficiente.

¿Y las tablas con organización por columnas?

Las tablas con organización por columnas tienen sus propios mecanismos de compresión. Ese tema es importante, pero no es el ámbito de esta lab.

Para este artículo:

  • Vamos a trabajar solo con tablas cuyos datos están organizados por filas (TABLEORG = 'R')
  • La función SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO es específica de row compression y devuelve cero filas para tablas column-organized

4. Crear la base de datos de la lab

Antes de crear la base de datos, cámbiate a la cuenta propietaria de la instancia Db2.

Exemplo:

su - db2inst1

Si en tu entorno el usuario propietario de la instancia tiene otro nombre, sustituye db2inst1 por el nombre correcto.

Ahora crea la base de datos:

db2 "create database COMPLAB pagesize 32768"

5. Crear los objetos del schema

Vamos a crear los objetos de un escenario de negocio simple, pero suficiente para mostrar compresión en datos e índices.

Además de las tablas principales, vamos a crear una tabla pequeña y una tabla volatile que quedarán excluidas del análisis de compresión.

cat > /tmp/complab_schema.sql <<'SQL'
CONNECT TO COMPLAB;

CREATE SCHEMA biz;

CREATE TABLE biz.customer_master
(
    customer_id        INTEGER NOT NULL,
    customer_name      VARCHAR(80) NOT NULL,
    segment            VARCHAR(20) NOT NULL,
    country_code       CHAR(2) NOT NULL,
    region_name        VARCHAR(20) NOT NULL,
    email_address      VARCHAR(120),
    phone_number       VARCHAR(24),
    marketing_opt_in   SMALLINT NOT NULL DEFAULT 0,
    credit_class       CHAR(1) NOT NULL DEFAULT 'B',
    status_code        CHAR(1) NOT NULL DEFAULT 'A',
    comments           VARCHAR(160),
    created_ts         TIMESTAMP NOT NULL,
    filler_code        CHAR(12) NOT NULL DEFAULT 'STANDARD',
    PRIMARY KEY (customer_id)
);

CREATE TABLE biz.sales_order
(
    order_id           BIGINT NOT NULL,
    customer_id        INTEGER NOT NULL,
    order_date         DATE NOT NULL,
    channel_code       VARCHAR(12) NOT NULL,
    order_status       VARCHAR(16) NOT NULL,
    country_code       CHAR(2) NOT NULL,
    currency_code      CHAR(3) NOT NULL,
    payment_method     VARCHAR(20) NOT NULL,
    shipping_mode      VARCHAR(20) NOT NULL,
    sales_rep          VARCHAR(30) NOT NULL,
    promo_code         VARCHAR(20),
    approval_code      CHAR(12),
    gift_message       VARCHAR(120),
    remarks            VARCHAR(200),
    fraud_score        SMALLINT NOT NULL DEFAULT 0,
    total_amount       DECIMAL(13,2) NOT NULL,
    last_update_ts     TIMESTAMP NOT NULL,
    PRIMARY KEY (order_id)
);

CREATE TABLE biz.sales_order_line
(
    line_id            BIGINT NOT NULL,
    order_id           BIGINT NOT NULL,
    line_no            SMALLINT NOT NULL,
    sku_code           VARCHAR(30) NOT NULL,
    product_family     VARCHAR(30) NOT NULL,
    warehouse_code     CHAR(4) NOT NULL,
    tax_code           CHAR(3) NOT NULL,
    quantity           SMALLINT NOT NULL,
    unit_price         DECIMAL(11,2) NOT NULL,
    discount_pct       DECIMAL(5,2) NOT NULL DEFAULT 0,
    serial_number      VARCHAR(40),
    line_comment       VARCHAR(120),
    backorder_flag     SMALLINT NOT NULL DEFAULT 0,
    fulfilled_flag     SMALLINT NOT NULL DEFAULT 1,
    PRIMARY KEY (line_id)
);

CREATE TABLE biz.small_reference
(
    ref_code           CHAR(4) NOT NULL,
    ref_group          VARCHAR(20) NOT NULL,
    ref_description    VARCHAR(80) NOT NULL,
    PRIMARY KEY (ref_code)
);

CREATE TABLE biz.session_work_queue
(
    queue_id           BIGINT NOT NULL,
    session_token      CHAR(24) NOT NULL,
    worker_name        VARCHAR(30) NOT NULL,
    payload_type       VARCHAR(20) NOT NULL,
    payload_ref        VARCHAR(40),
    status_code        CHAR(1) NOT NULL,
    created_ts         TIMESTAMP NOT NULL,
    PRIMARY KEY (queue_id)
);

ALTER TABLE biz.session_work_queue VOLATILE CARDINALITY;

CREATE TABLE biz.tab_comp_baseline
(
    tabname            VARCHAR(128) NOT NULL,
    pre_npages         INTEGER NOT NULL,
    pagesize           INTEGER NOT NULL,
    st_pct             SMALLINT NOT NULL,
    ad_pct             SMALLINT NOT NULL,
    st_mb              DECIMAL(20,2) NOT NULL,
    ad_mb              DECIMAL(20,2) NOT NULL,
    PRIMARY KEY (tabname)
);

CREATE INDEX biz.ix_order_customer_date
    ON biz.sales_order (customer_id, order_date);

CREATE INDEX biz.ix_order_status_country
    ON biz.sales_order (order_status, country_code);

CREATE INDEX biz.ix_order_salesrep
    ON biz.sales_order (sales_rep);

CREATE INDEX biz.ix_line_order
    ON biz.sales_order_line (order_id);

CREATE INDEX biz.ix_line_sku
    ON biz.sales_order_line (sku_code);

CREATE INDEX biz.ix_line_wh_family
    ON biz.sales_order_line (warehouse_code, product_family);

CREATE INDEX biz.ix_queue_status
    ON biz.session_work_queue (status_code, created_ts);

COMMIT;
CONNECT RESET;
SQL

db2 -tvf /tmp/complab_schema.sql

6. Generar y cargar datos representativos

Vamos a cargar datos con patrones típicos de negocio:

  • Códigos de país, canales, métodos de pago y estados con fuerte repetición
  • Columnas opcionales con muchos NULL
  • Textos semirrepetitivos
  • Volumen suficiente para que la compresión muestre resultados visibles
  • Una tabla pequeña y una tabla VOLATILE que no justifican evaluación

6.1 Cargar customer_master

cat > /tmp/complab_load_customers.sql <<'SQL'
CONNECT TO COMPLAB;

INSERT INTO biz.customer_master
WITH d(n) AS
(
    VALUES 0,1,2,3,4,5,6,7,8,9
),
nums(n) AS
(
    SELECT
        a.n * 10000 +
        b.n * 1000 +
        c.n * 100 +
        d1.n * 10 +
        e.n + 1
    FROM d a, d b, d c, d d1, d e
    WHERE
        a.n * 10000 +
        b.n * 1000 +
        c.n * 100 +
        d1.n * 10 +
        e.n + 1 <= 100000
)
SELECT
    n,
    'Customer ' || VARCHAR(n),
    CASE MOD(n, 5)
        WHEN 0 THEN 'Enterprise'
        WHEN 1 THEN 'Corporate'
        WHEN 2 THEN 'SMB'
        WHEN 3 THEN 'Retail'
        ELSE 'Partner'
    END,
    CASE MOD(n, 6)
        WHEN 0 THEN 'PT'
        WHEN 1 THEN 'ES'
        WHEN 2 THEN 'FR'
        WHEN 3 THEN 'DE'
        WHEN 4 THEN 'IT'
        ELSE 'NL'
    END,
    CASE MOD(n, 6)
        WHEN 0 THEN 'Iberia'
        WHEN 1 THEN 'Iberia'
        WHEN 2 THEN 'France'
        WHEN 3 THEN 'DACH'
        WHEN 4 THEN 'SouthEU'
        ELSE 'Benelux'
    END,
    CASE
        WHEN MOD(n, 20) = 0 THEN NULL
        ELSE 'contact' || VARCHAR(MOD(n, 12000)) || '@example.com'
    END,
    CASE
        WHEN MOD(n, 15) = 0 THEN NULL
        ELSE '+351210' || RIGHT(DIGITS(100000 + MOD(n, 900000)), 6)
    END,
    CASE WHEN MOD(n, 4) = 0 THEN 1 ELSE 0 END,
    CASE MOD(n, 4)
        WHEN 0 THEN 'A'
        WHEN 1 THEN 'B'
        WHEN 2 THEN 'B'
        ELSE 'C'
    END,
    CASE WHEN MOD(n, 25) = 0 THEN 'H' ELSE 'A' END,
    CASE
        WHEN MOD(n, 7) = 0 THEN 'Annual contract customer'
        WHEN MOD(n, 11) = 0 THEN 'Prefers consolidated monthly invoice'
        ELSE 'Standard sales account'
    END,
    TIMESTAMP('2023-01-01-08.00.00') + MOD(n, 700) DAYS,
    CASE WHEN MOD(n, 10) = 0 THEN 'STANDARD' ELSE 'STANDARD' END
FROM nums
WHERE NOT EXISTS
(
    SELECT 1
    FROM biz.customer_master c
    WHERE c.customer_id = nums.n
);

COMMIT;
CONNECT RESET;
SQL

db2 -tvf /tmp/complab_load_customers.sql

6.2 Cargar sales_order

Este bloque de código crea 500000 pedidos con muchos patrones repetidos y algunas columnas dispersas. La carga se hace en lotes de 25000 filas con COMMIT entre lotes, para evitar SQL0964C en entornos con configuración de log más conservadora. NOT EXISTS hace que la carga sea repetible sin generar errores de clave duplicada.

Dentro de cada lote de 25000 filas, creamos micro-lotes locales de 250 pedidos. En cada micro-lote, varias columnas de texto largas repiten los mismos patrones locales y el mismo identificador local muchas veces, pero esos patrones cambian en el micro-lote siguiente. Hacemos esto porque queremos reproducir un escenario en el que la compresión adaptativa sea más ventajosa que la compresión estática.

db2 connect to COMPLAB

for start_n in 1 25001 50001 75001 100001 125001 150001 175001 200001 225001 250001 275001 300001 325001 350001 375001 400001 425001 450001 475001
do
  end_n=$((start_n + 24999))

  db2 "
  INSERT INTO biz.sales_order
  WITH d(n) AS
  (
      VALUES 0,1,2,3,4,5,6,7,8,9
  ),
  nums(n) AS
  (
      SELECT
          a.n * 100000 +
          b.n * 10000 +
          c.n * 1000 +
          d1.n * 100 +
          e.n * 10 +
          f.n + 1
      FROM d a, d b, d c, d d1, d e, d f
      WHERE
          a.n * 100000 +
          b.n * 10000 +
          c.n * 1000 +
          d1.n * 100 +
          e.n * 10 +
          f.n + 1 BETWEEN ${start_n} AND ${end_n}
  )
  SELECT
      BIGINT(n),
      1 + MOD(n, 100000),
      DATE('2023-01-01') + MOD(n, 730) DAYS,
      CASE MOD(n, 4)
          WHEN 0 THEN 'ONLINE'
          WHEN 1 THEN 'PARTNER'
          WHEN 2 THEN 'DIRECT'
          ELSE 'FIELD'
      END,
      CASE MOD(n, 6)
          WHEN 0 THEN 'CREATED'
          WHEN 1 THEN 'PAID'
          WHEN 2 THEN 'ALLOCATED'
          WHEN 3 THEN 'SHIPPED'
          WHEN 4 THEN 'CLOSED'
          ELSE 'CLOSED'
      END,
      CASE MOD(n, 6)
          WHEN 0 THEN 'PT'
          WHEN 1 THEN 'ES'
          WHEN 2 THEN 'FR'
          WHEN 3 THEN 'DE'
          WHEN 4 THEN 'IT'
          ELSE 'NL'
      END,
      CASE MOD(n, 3)
          WHEN 0 THEN 'EUR'
          WHEN 1 THEN 'EUR'
          ELSE 'USD'
      END,
      CASE MOD(n, 5)
          WHEN 0 THEN 'CARD'
          WHEN 1 THEN 'CARD'
          WHEN 2 THEN 'TRANSFER'
          WHEN 3 THEN 'TRANSFER'
          ELSE 'INVOICE'
      END,
      CASE MOD(n, 4)
          WHEN 0 THEN 'STANDARD'
          WHEN 1 THEN 'STANDARD'
          WHEN 2 THEN 'EXPRESS'
          ELSE 'PICKUP'
      END,
      'REP_' || RIGHT(DIGITS(100 + MOD(n, 35)), 3),
      CASE
          WHEN MOD(n, 5) IN (0,1,2,3) THEN
              'L'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || '-P'
              || RIGHT(DIGITS(1000 + MOD(INTEGER((n - 1) / 250), 97)), 4)
          WHEN MOD(n, 15) = 0 THEN
              'X'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || '-P'
              || RIGHT(DIGITS(1000 + MOD(INTEGER((n - 1) / 250), 53)), 4)
          ELSE NULL
      END,
      CASE
          WHEN MOD(n, 6) IN (0,1,2,3) THEN 'APR' || RIGHT(DIGITS(100000000 + MOD(n, 900000000)), 9)
          ELSE NULL
      END,
      CASE
          WHEN MOD(n, 6) <> 5 THEN
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' wrap pack LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' insert note LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' carrier tag LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
          ELSE NULL
      END,
      CASE
          WHEN MOD(n, 10) = 0 THEN
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' route bundle LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' route bundle LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' route bundle LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' route bundle priority desk hold'
          WHEN MOD(n, 14) = 0 THEN
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' split stage LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' split stage LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' split stage LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' split stage late dock'
          ELSE
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' pack wave LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' pack wave LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' pack wave LOC'
              || RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
              || ' pack wave regular dock'
      END,
      CASE
          WHEN MOD(n, 50) = 0 THEN 90
          WHEN MOD(n, 10) = 0 THEN 30
          ELSE 0
      END,
      DECIMAL(20 + MOD(n, 8000) + (MOD(n, 100) / 100.0), 13, 2),
      TIMESTAMP('2024-01-01-09.00.00') + MOD(n, 365) DAYS + MOD(n, 86400) SECONDS
  FROM nums nsrc
  WHERE NOT EXISTS
  (
      SELECT 1
      FROM biz.sales_order so
      WHERE so.order_id = BIGINT(nsrc.n)
  )"

  db2 commit
done

db2 connect reset

6.3 Cargar sales_order_line

Este bloque de código genera dos filas por pedido, lo que producirá 1000000 de filas con repetición útil para compresión de datos y de índices. También aquí usamos lotes con COMMIT, y el filtro NOT EXISTS evita duplicados si el bloque se repite. En este caso usamos bloques de 25000 pedidos cada vez.

Igual que en sales_order, introducimos patrones largos que se repiten con fuerza por micro-lotes locales de 250 pedidos, pero que cambian en el micro-lote siguiente.

db2 connect to COMPLAB

for start_id in 1 25001 50001 75001 100001 125001 150001 175001 200001 225001 250001 275001 300001 325001 350001 375001 400001 425001 450001 475001
do
  end_id=$((start_id + 24999))

  db2 "
  INSERT INTO biz.sales_order_line
  SELECT
      BIGINT(o.order_id * 10 + l.line_no),
      o.order_id,
      l.line_no,
      'SKU-' || RIGHT(DIGITS(100000 + MOD(o.order_id + l.line_no, 2500)), 6),
      CASE MOD(o.order_id + l.line_no, 6)
          WHEN 0 THEN 'LAPTOP'
          WHEN 1 THEN 'SERVER'
          WHEN 2 THEN 'STORAGE'
          WHEN 3 THEN 'NETWORK'
          WHEN 4 THEN 'SOFTWARE'
          ELSE 'SERVICE'
      END,
      CASE MOD(o.order_id + l.line_no, 4)
          WHEN 0 THEN 'LISB'
          WHEN 1 THEN 'MADR'
          WHEN 2 THEN 'PARI'
          ELSE 'FRAN'
      END,
      CASE MOD(o.order_id + l.line_no, 3)
          WHEN 0 THEN 'NOR'
          WHEN 1 THEN 'RED'
          ELSE 'RED'
      END,
      SMALLINT(1 + MOD(o.order_id + l.line_no, 5)),
      DECIMAL(15 + MOD(o.order_id + l.line_no, 900) + (MOD(o.order_id, 100) / 100.0), 11, 2),
      DECIMAL(CASE WHEN MOD(o.order_id + l.line_no, 8) = 0 THEN 10.00 ELSE 0.00 END, 5, 2),
      CASE
          WHEN MOD(o.order_id + l.line_no, 20) = 0 THEN
              'SN'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || '-'
              || RIGHT(DIGITS(100000 + MOD(o.order_id, 900000)), 6)
          ELSE NULL
      END,
      CASE
          WHEN MOD(o.order_id + l.line_no, 9) = 0 THEN
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' bundle step LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' bundle step LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' bundle step'
          ELSE
              'LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' pick step LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' pick step LOC'
              || RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
              || ' pick step'
      END,
      CASE WHEN MOD(o.order_id + l.line_no, 17) = 0 THEN 1 ELSE 0 END,
      CASE WHEN MOD(o.order_id + l.line_no, 13) = 0 THEN 0 ELSE 1 END
  FROM biz.sales_order o,
       (VALUES 1, 2) AS l(line_no)
  WHERE o.order_id BETWEEN ${start_id} AND ${end_id}
    AND NOT EXISTS
    (
        SELECT 1
        FROM biz.sales_order_line sol
        WHERE sol.line_id = BIGINT(o.order_id * 10 + l.line_no)
    )"

  db2 commit
done

db2 connect reset

6.4 Carga de la tabla pequeña y de la tabla VOLATILE

cat > /tmp/complab_load_auxiliary.sql <<'SQL'
CONNECT TO COMPLAB;

INSERT INTO biz.small_reference
SELECT *
FROM
(
    VALUES
    ('PT01', 'COUNTRY', 'Portugal'),
    ('ES01', 'COUNTRY', 'Spain'),
    ('FR01', 'COUNTRY', 'France'),
    ('DE01', 'COUNTRY', 'Germany'),
    ('IT01', 'COUNTRY', 'Italy'),
    ('NL01', 'COUNTRY', 'Netherlands'),
    ('ONL1', 'CHANNEL', 'Online direct channel'),
    ('PAR1', 'CHANNEL', 'Partner resale channel'),
    ('DIR1', 'CHANNEL', 'Direct sales channel'),
    ('FLD1', 'CHANNEL', 'Field account channel')
) AS src(ref_code, ref_group, ref_description)
WHERE NOT EXISTS
(
    SELECT 1
    FROM biz.small_reference r
    WHERE r.ref_code = src.ref_code
);

INSERT INTO biz.session_work_queue
WITH d(n) AS
(
    VALUES 0,1,2,3,4,5,6,7,8,9
),
nums(n) AS
(
    SELECT
        a.n * 1000 +
        b.n * 100 +
        c.n * 10 +
        d1.n + 1
    FROM d a, d b, d c, d d1
    WHERE
        a.n * 1000 +
        b.n * 100 +
        c.n * 10 +
        d1.n + 1 <= 5000
)
SELECT
    BIGINT(n),
    'TOK' || RIGHT(DIGITS(100000000 + MOD(n, 900000000)), 8) || RIGHT(DIGITS(100000000 + MOD(n * 7, 900000000)), 8) || RIGHT(DIGITS(1000 + MOD(n, 9000)), 5),
    'WORKER_' || RIGHT(DIGITS(100 + MOD(n, 20)), 3),
    CASE MOD(n, 4)
        WHEN 0 THEN 'ALLOC'
        WHEN 1 THEN 'PICK'
        WHEN 2 THEN 'PACK'
        ELSE 'SHIP'
    END,
    CASE
        WHEN MOD(n, 5) = 0 THEN 'ORD-' || VARCHAR(100000 + MOD(n, 500000))
        ELSE NULL
    END,
    CASE MOD(n, 3)
        WHEN 0 THEN 'N'
        WHEN 1 THEN 'R'
        ELSE 'D'
    END,
    TIMESTAMP('2026-01-01-00.00.00') + MOD(n, 30) DAYS + MOD(n, 86400) SECONDS
FROM nums
WHERE NOT EXISTS
(
    SELECT 1
    FROM biz.session_work_queue q
    WHERE q.queue_id = BIGINT(nums.n)
);

COMMIT;
CONNECT RESET;
SQL

db2 -tvf /tmp/complab_load_auxiliary.sql

7. Recoger estadísticas y establecer una baseline

Antes de estimar compresión, actualizamos las estadísticas:

cat > /tmp/complab_runstats_before.sql <<'SQL'
CONNECT TO COMPLAB;

RUNSTATS ON TABLE biz.customer_master WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order_line WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.small_reference WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.session_work_queue WITH DISTRIBUTION AND DETAILED INDEXES ALL;

CONNECT RESET;
SQL

db2 -tvf /tmp/complab_runstats_before.sql

8. Estimación de compresión para tablas sin compresión activa

Esta es la primera situación operativa importante: tablas con organización por filas y todavía sin compresión activa.

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
npgNPAGES actual
cardCardinalidad estimada de la tabla
pszTamaño de página del table space
modeModo actual de row compression
cur_pctPorcentaje actual de páginas ahorradas
st_pctEstimación de ahorro con STATIC
st_rowTamaño medio estimado de la fila con STATIC
st_pagesPáginas estimadas ahorradas con STATIC
st_mbMB estimados ahorrados con STATIC
ad_pctEstimación de ahorro con ADAPTIVE
ad_rowTamaño medio estimado de la fila con ADAPTIVE
ad_pagesPáginas estimadas ahorradas con ADAPTIVE
ad_mbMB estimados ahorrados con ADAPTIVE
db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
    CAST(c.npages AS VARCHAR(8)) AS npg,
    CAST(c.card AS VARCHAR(12)) AS card,
    CAST(c.pagesize AS VARCHAR(8)) AS psz,
    CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
    CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
    CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
    CAST(ci.avgrowsize_static AS VARCHAR(8)) AS st_row,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0), 20, 2) AS VARCHAR(12)) AS st_pages,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS st_mb,
    CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
    CAST(ci.avgrowsize_adaptive AS VARCHAR(8)) AS ad_row,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0), 20, 2) AS VARCHAR(12)) AS ad_pages,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS ad_mb
FROM
(
    SELECT
        t.tabschema,
        t.tabname,
        t.npages,
        t.card,
        ts.pagesize
    FROM syscat.tables t
    JOIN syscat.tablespaces ts
      ON ts.tbspace = t.tbspace
    WHERE t.tabschema = 'BIZ'
      AND t.type = 'T'
      AND t.tableorg = 'R'
      AND t.compression = 'N'
      AND t.card > 0
) AS c,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY c.npages DESC"
db2 connect reset

Interpretación:

  • st_pct estima la ganancia si activas classic row compression
  • ad_pct estima la ganancia si activas adaptive compression
  • st_mb y ad_mb traducen el porcentaje a espacio físico potencialmente ahorrado

9. Estimación por schema con filtrado previo

Esta es la forma más útil cuando quieres trabajar por lotes, pero sin evaluar todo indiscriminadamente. El ejemplo siguiente filtra previamente por schema, tamaño mínimo de tabla y excluye tablas volátiles. Para mantener la lectura enfocada en la decisión, la salida muestra solo las columnas que ayudan a comparar rápidamente STATIC y ADAPTIVE.

db2 connect to COMPLAB
db2 "
WITH candidates AS
(
    SELECT
        t.tabschema,
        t.tabname,
        t.npages,
        t.card,
        ts.pagesize
    FROM syscat.tables t
    JOIN syscat.tablespaces ts
      ON ts.tbspace = t.tbspace
    WHERE t.tabschema = 'BIZ'
      AND t.type = 'T'
      AND t.tableorg = 'R'
      AND t.card > 0
      AND t.npages > 1000
      AND t.compression = 'N'
      AND COALESCE(t.volatile, ' ') <> 'C'
)
SELECT
    CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
    CAST(c.npages AS VARCHAR(8)) AS npg,
    CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0), 20, 2) AS VARCHAR(12)) AS st_pages,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS st_mb,
    CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0), 20, 2) AS VARCHAR(12)) AS ad_pages,
    CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS ad_mb
FROM candidates c,
     TABLE
     (
         SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)
     ) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY
    c.npages DESC,
    c.card DESC"
db2 connect reset

Notas prácticas sobre los filtros:

  • t.tableorg = 'R' impide que tablas con organización por columnas entren en el análisis
  • t.npages > 1000 evita perder tiempo con objetos demasiado pequeños
  • COALESCE(t.volatile, ' ') <> 'C' deja fuera tablas declaradas como volátiles

Antes de avanzar, conviene verificar explícitamente por qué cada tabla se incluye o se excluye del análisis:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
    CAST(npages AS VARCHAR(8)) AS npg,
    CAST(COALESCE(volatile, ' ') AS VARCHAR(4)) AS vol,
    CAST(compression AS VARCHAR(4)) AS comp,
    CASE
        WHEN tableorg <> 'R' THEN 'excluded: not row-organized'
        WHEN card = 0 THEN 'excluded: no useful statistics'
        WHEN COALESCE(volatile, ' ') = 'C' THEN 'excluded: volatile table'
        WHEN npages <= 1000 THEN 'excluded: too small'
        WHEN compression <> 'N' THEN 'excluded: already compressed'
        ELSE 'included in evaluation'
    END AS reason
FROM syscat.tables
WHERE tabschema = 'BIZ'
  AND type = 'T'
ORDER BY tabname"
db2 connect reset

En esta lab deberías observar, en condiciones normales:

  • SMALL_REFERENCE excluida por tamaño
  • SESSION_WORK_QUEUE excluida por estar marcada como VOLATILE
  • CUSTOMER_MASTER, SALES_ORDER y SALES_ORDER_LINE incluidas en el lote de evaluación

10. Comparar STATIC y ADAPTIVE antes de la compresión

Antes de activar compresión, este análisis permite comparar directamente STATIC y ADAPTIVE y elegir el modo más ventajoso para cada tabla.

db2 connect to COMPLAB
db2 "
WITH candidates AS
(
    SELECT
        t.tabschema,
        t.tabname,
        t.npages,
        t.card,
        t.compression,
        ts.pagesize
    FROM syscat.tables t
    JOIN syscat.tablespaces ts
      ON ts.tbspace = t.tbspace
    WHERE t.tabschema = 'BIZ'
      AND t.type = 'T'
      AND t.tableorg = 'R'
      AND t.card > 0
      AND t.npages > 1000
      AND t.compression = 'N'
      AND COALESCE(t.volatile, ' ') <> 'C'
)
SELECT
    CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
    CAST(c.npages AS VARCHAR(8)) AS npg,
    CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
    CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
    CAST(ci.pctpagessaved_adaptive - ci.pctpagessaved_static AS VARCHAR(6)) AS adv_pct,
    CAST(DECIMAL(
        (DECIMAL(c.npages,20,2) * (ci.pctpagessaved_adaptive - ci.pctpagessaved_static) / 100.0) * c.pagesize / 1048576.0,
        20, 2
    ) AS VARCHAR(10)) AS extra_mb
FROM candidates c,
     TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY adv_pct DESC, npg DESC"
db2 connect reset

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
npgNPAGES actual
st_pctEstimación con STATIC
ad_pctEstimación con ADAPTIVE
adv_pctDiferencia ADAPTIVE - STATIC en puntos porcentuales
extra_mbMB adicionales potencialmente ahorrados por ADAPTIVE

11. Identificar explícitamente dónde ADAPTIVE aporta ventaja

El resultado de la sección anterior sirve para elegir la tabla candidata principal. La lógica de la lab es la siguiente:

  • Si una tabla grande presenta un adv_pct material, esa tabla es la mejor candidata para compresión adaptativa
  • Si otra tabla también se beneficia bastante de la compresión en términos absolutos, pero tiene una diferencia menor entre STATIC y ADAPTIVE, sigue siendo un buen objetivo de compresión, pero puede empezar por STATIC
  • Después de activar la compresión y reorganizar, los datos medidos deben confirmar estas dos ideas: la tabla elegida para ADAPTIVE debería acercarse a la estimación adaptativa, y la tabla dejada en STATIC puede seguir mostrando margen adicional potencial en el análisis final

En el escenario buscado para esta lab, ambas tablas grandes deben mostrar ganancias fuertes con compresión, pero sales_order debería evidenciar la ventaja más clara de la compresión adaptativa.

12. Estimar ganancias para índices sin compresión

Para índices, el caso de uso clásico es simple: índices sin compresión física, en tablas grandes, con bastante repetición de claves o prefijos.

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
    CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
    CAST(compress_attr AS VARCHAR(4)) AS attr,
    CAST(index_compressed AS VARCHAR(4)) AS icomp,
    CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
    CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
    SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', 'SALES_ORDER', NULL, NULL)
) AS t
ORDER BY num_leaf_pages_saved DESC"
db2 connect reset

Interpretación:

  • COMPRESS_ATTR = 'N' e INDEX_COMPRESSED = 'N' significa índice sin compresión
  • PCT_PAGES_SAVED estima el porcentaje potencial de páginas leaf ahorradas
  • NUM_LEAF_PAGES_SAVED traduce la ganancia a páginas físicas

Leyenda de los encabezados:

EncabezadoSignificado
idxNombre del índice
tabTabla base
attrAtributo de compresión definido para el índice
icompEstado actual de compresión del índice
pct_savedPorcentaje estimado de páginas leaf ahorradas
leaf_savedNúmero estimado de páginas leaf ahorradas

Puedes repetir el mismo análisis para otra tabla:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
    CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
    CAST(compress_attr AS VARCHAR(4)) AS attr,
    CAST(index_compressed AS VARCHAR(4)) AS icomp,
    CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
    CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
    SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', 'SALES_ORDER_LINE', NULL, NULL)
) AS t
ORDER BY num_leaf_pages_saved DESC"
db2 connect reset

13. Activar compresión en la tabla y en los índices

Ahora convertimos la decisión anterior en acciones concretas:

  • sales_order se comprimirá con ADAPTIVE, porque la estimación muestra una ventaja visible frente a STATIC
  • sales_order_line se comprimirá con STATIC, no porque comprima poco, sino porque la diferencia inicial frente a ADAPTIVE es menor y queremos validar después si todavía existe margen adicional
  • Las columnas con defaults repetidos activan value compression
  • Los índices relevantes también pasan a COMPRESS YES

Antes del cambio, guardamos una baseline persistente para las dos tablas. Esa baseline se usará más adelante para comparar la estimación previa con el resultado real medido después del REORG:

db2 connect to COMPLAB
db2 "
MERGE INTO biz.tab_comp_baseline AS b
USING
(
    SELECT
        t.tabname,
        t.npages,
        ts.pagesize,
        ci.pctpagessaved_static AS st_pct,
        ci.pctpagessaved_adaptive AS ad_pct,
        DECIMAL((DECIMAL(t.npages,20,2) * ci.pctpagessaved_static / 100.0) * ts.pagesize / 1048576.0, 20, 2) AS st_mb,
        DECIMAL((DECIMAL(t.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * ts.pagesize / 1048576.0, 20, 2) AS ad_mb
    FROM syscat.tables t
    JOIN syscat.tablespaces ts
      ON ts.tbspace = t.tbspace,
         TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
    WHERE t.tabschema = 'BIZ'
      AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
      AND ci.object_type = 'DATA'
) AS src
ON b.tabname = src.tabname
WHEN MATCHED THEN
    UPDATE SET
        pre_npages = src.npages,
        pagesize = src.pagesize,
        st_pct = src.st_pct,
        ad_pct = src.ad_pct,
        st_mb = src.st_mb,
        ad_mb = src.ad_mb
WHEN NOT MATCHED THEN
    INSERT (tabname, pre_npages, pagesize, st_pct, ad_pct, st_mb, ad_mb)
    VALUES (src.tabname, src.npages, src.pagesize, src.st_pct, src.ad_pct, src.st_mb, src.ad_mb)";

db2 "
SELECT
    CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
    CAST(pre_npages AS VARCHAR(8)) AS pre_npg,
    CAST(st_pct AS VARCHAR(6)) AS st_pct,
    CAST(ad_pct AS VARCHAR(6)) AS ad_pct,
    CAST(st_mb AS VARCHAR(10)) AS st_mb,
    CAST(ad_mb AS VARCHAR(10)) AS ad_mb
FROM biz.tab_comp_baseline
WHERE tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
ORDER BY tabname"
db2 connect reset

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
pre_npgNPAGES antes de la compresión
st_pctEstimación con STATIC antes de la compresión
ad_pctEstimación con ADAPTIVE antes de la compresión
st_mbMB estimados ahorrados con STATIC antes de la compresión
ad_mbMB estimados ahorrados con ADAPTIVE antes de la compresión

Vamos a modificar las tablas para activar la compresión.

cat > /tmp/complab_enable_compression.sql <<'SQL'
CONNECT TO COMPLAB;

ALTER TABLE biz.sales_order
    ALTER fraud_score COMPRESS SYSTEM DEFAULT
    COMPRESS YES ADAPTIVE
    ACTIVATE VALUE COMPRESSION;

ALTER TABLE biz.sales_order_line
    ALTER discount_pct COMPRESS SYSTEM DEFAULT
    ALTER backorder_flag COMPRESS SYSTEM DEFAULT
    ALTER fulfilled_flag COMPRESS SYSTEM DEFAULT
    COMPRESS YES STATIC
    ACTIVATE VALUE COMPRESSION;

ALTER INDEX biz.ix_order_customer_date COMPRESS YES;
ALTER INDEX biz.ix_order_status_country COMPRESS YES;
ALTER INDEX biz.ix_order_salesrep COMPRESS YES;
ALTER INDEX biz.ix_line_order COMPRESS YES;
ALTER INDEX biz.ix_line_sku COMPRESS YES;
ALTER INDEX biz.ix_line_wh_family COMPRESS YES;

COMMIT;
CONNECT RESET;
SQL

db2 -tvf /tmp/complab_enable_compression.sql

14. Materializar los datos comprimidos

Activar las cláusulas de compresión no basta por sí solo para reformatear inmediatamente todos los datos antiguos. Para aplicar la nueva organización física, tenemos que reorganizar.

cat > /tmp/complab_reorg.sql <<'SQL'
CONNECT TO COMPLAB;

REORG TABLE biz.sales_order RESETDICTIONARY;
REORG INDEXES ALL FOR TABLE biz.sales_order ALLOW WRITE ACCESS;

REORG TABLE biz.sales_order_line RESETDICTIONARY;
REORG INDEXES ALL FOR TABLE biz.sales_order_line ALLOW WRITE ACCESS;

RUNSTATS ON TABLE biz.sales_order WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order_line WITH DISTRIBUTION AND DETAILED INDEXES ALL;

CONNECT RESET;
SQL

db2 -tvf /tmp/complab_reorg.sql

15. Verificar las mejoras reales, con foco en SALES_ORDER

Primero confirmamos si la tabla elegida pasó efectivamente a compresión adaptativa y comparamos el ahorro actual con la baseline guardada:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
    CAST(b.pre_npages AS VARCHAR(8)) AS pre_npg,
    CAST(t.npages AS VARCHAR(8)) AS cur_npg,
    CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
    CAST(b.st_pct AS VARCHAR(6)) AS pre_st,
    CAST(b.ad_pct AS VARCHAR(6)) AS pre_ad,
    CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
    CAST(b.ad_mb AS VARCHAR(10)) AS pre_ad_mb,
    CAST(DECIMAL
    (
        CASE
            WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
            THEN ((DECIMAL(t.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current) - DECIMAL(t.npages,20,4)) * ts.pagesize / 1048576.0
            ELSE NULL
        END,
        20, 2
    ) AS VARCHAR(10)) AS cur_mb
FROM syscat.tables t
JOIN syscat.tablespaces ts
  ON ts.tbspace = t.tbspace
JOIN biz.tab_comp_baseline b
  ON b.tabname = t.tabname,
     TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
  AND t.tabname = 'SALES_ORDER'
  AND ci.object_type = 'DATA'"
db2 connect reset

El punto a verificar aquí es simple:

  • ROWCOMPMODE debe aparecer como A.
  • PCTPAGESSAVED_CURRENT debe aproximarse a la baseline pre_ad.
  • cur_mb debe aproximarse a pre_ad_mb, dentro de la variación normal causada por páginas parcialmente llenas y por el estado real del objeto.

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
pre_npgNPAGES antes de la compresión
cur_npgNPAGES después de la compresión
modeModo actual de row compression
pre_stBaseline STATIC antes de la compresión
pre_adBaseline ADAPTIVE antes de la compresión
cur_pctAhorro actual medido
pre_ad_mbMB estimados con ADAPTIVE antes de la compresión
cur_mbMB ahorrados actualmente

Solo después de eso tiene sentido mirar el conjunto completo.

16. Validar el resultado real después de la compresión

Primero confirmamos los atributos de las tablas:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
    CAST(t.compression AS VARCHAR(4)) AS comp,
    CAST(t.tableorg AS VARCHAR(4)) AS org,
    CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
    CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct
FROM syscat.tables t,
     TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
  AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
  AND ci.object_type = 'DATA'"
db2 connect reset

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
compAtributo de compresión en el catálogo
orgOrganización de la tabla
modeModo actual de row compression
cur_pctAhorro actual medido

Después comparamos la ganancia actual con las estimaciones guardadas en la baseline:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
    CAST(b.pre_npages AS VARCHAR(8)) AS pre_npg,
    CAST(t.npages AS VARCHAR(8)) AS cur_npg,
    CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
    CAST(b.st_pct AS VARCHAR(6)) AS pre_st,
    CAST(b.ad_pct AS VARCHAR(6)) AS pre_ad,
    CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
    CAST(b.st_mb AS VARCHAR(10)) AS pre_st_mb,
    CAST(b.ad_mb AS VARCHAR(10)) AS pre_ad_mb,
    CAST(DECIMAL
    (
        CASE
            WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
            THEN ((DECIMAL(t.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current) - DECIMAL(t.npages,20,4)) * ts.pagesize / 1048576.0
            ELSE NULL
        END,
        20, 2
    ) AS VARCHAR(10)) AS cur_mb
FROM syscat.tables t
JOIN syscat.tablespaces ts
  ON ts.tbspace = t.tbspace
JOIN biz.tab_comp_baseline b
  ON b.tabname = t.tabname,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
  AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
  AND ci.object_type = 'DATA'"
db2 connect reset

Aquí la validación es importante:

  • sales_order debe mostrar mode = 'A' y un cur_pct más cercano a la baseline pre_ad que a pre_st
  • sales_order_line debe mostrar mode = 'S' y un cur_pct próximo a la baseline pre_st
  • Si sales_order_line todavía presenta una distancia visible entre cur_pct y pre_ad, eso prepara exactamente el análisis de la sección siguiente: ya existe compresión útil aplicada, pero todavía puede haber margen adicional con ADAPTIVE

Y por último validamos la compresión de los índices:

db2 connect to COMPLAB
db2 "
SELECT
    CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
    CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
    CAST(compress_attr AS VARCHAR(4)) AS attr,
    CAST(index_compressed AS VARCHAR(4)) AS icomp,
    CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
    CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
    SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', '', NULL, NULL)
) AS t
WHERE tabschema = 'BIZ'
ORDER BY tabname, indname"
db2 connect reset

Leyenda de los encabezados:

EncabezadoSignificado
idxNombre del índice
tabTabla base
attrAtributo de compresión configurado
icompEstado actual de compresión
pct_savedPorcentaje de páginas leaf ahorradas
leaf_savedNúmero de páginas leaf ahorradas

17. Verificar ganancias adicionales después de la compresión

En una tabla ya comprimida, ¿existe margen adicional si el modo actual no es el mejor, o si la ganancia actual todavía está lejos de la estimación adaptativa? Observando la tabla sales_order_line, verificamos que la compresión estática aportó una ganancia fuerte, pero todavía puede existir algún beneficio adicional si adoptamos compresión adaptativa.

db2 connect to COMPLAB
db2 "
WITH candidates AS
(
    SELECT
        t.tabschema,
        t.tabname,
        t.npages,
        t.card,
        t.compression,
        ts.pagesize
    FROM syscat.tables t
    JOIN syscat.tablespaces ts
      ON ts.tbspace = t.tbspace
    WHERE t.tabschema = 'BIZ'
      AND t.type = 'T'
      AND t.tableorg = 'R'
      AND t.card > 0
      AND t.compression <> 'N'
)
SELECT
    CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
    CAST(c.compression AS VARCHAR(4)) AS comp,
    CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
    CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
    CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
    CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
    CAST(DECIMAL
    (
        CASE
            WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
            THEN (DECIMAL(c.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current)
            ELSE NULL
        END,
        20, 2
    ) AS VARCHAR(12)) AS unc_pages,
    CAST(DECIMAL
    (
        CASE
            WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
            THEN
                (
                    ((DECIMAL(c.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current))
                    * (ci.pctpagessaved_adaptive - ci.pctpagessaved_current) / 100.0
                    * c.pagesize
                ) / 1048576.0
            ELSE NULL
        END,
        20, 2
    ) AS VARCHAR(10)) AS extra_mb
FROM candidates c,
     TABLE
     (
         SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)
     ) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY c.npages DESC"
db2 connect reset

Lectura del resultado:

  • Si cur_pct ya está próximo de ad_pct, el espacio adicional recuperable será pequeño.
  • Si mode = 'S' y ad_pct es claramente superior, esa es una situación concreta para considerar compresión adaptativa.

Leyenda de los encabezados:

EncabezadoSignificado
tabNombre de la tabla
compAtributo actual de compresión en el catálogo
modeModo actual de row compression (S, A o -)
cur_pctAhorro actual medido
st_pctEstimación con STATIC
ad_pctEstimación con ADAPTIVE
unc_pagesNúmero inferido de páginas sin compresión
extra_mbGanancia adicional estimada en MB si pasas a ADAPTIVE

18. Resumen

Esta lab mostró varios puntos operativos importantes:

  • La compresión de tablas con datos organizados por filas en Db2 no recurre a una sola funcionalidad. Involucra compresión clásica estática, compresión adaptativa y compresión de valores.
  • ADMIN_GET_TAB_COMPRESS_INFO y ADMIN_GET_INDEX_COMPRESS_INFO permiten estimar las ganancias con compresión.
  • No todas las tablas deben entrar en el análisis. En esta lab, una fue excluida por ser demasiado pequeña y otra por estar marcada como VOLATILE

Una secuencia adecuada suele ser:

1. Filtrar candidatos en función de tamaño, volatilidad y patrón de uso 2. Estimar ganancias para tablas e índices 3. Activar compresión solo en los objetos con retorno claro 4. Reorganizar y recoger estadísticas 5. Medir de nuevo

19. Limpieza

Cuando termines la lab, elimina la base de datos y los archivos temporales:

db2 force applications all
db2 drop database COMPLAB
rm -f /tmp/complab_schema.sql \
      /tmp/complab_load_customers.sql \
      /tmp/complab_load_orders.sql \
      /tmp/complab_load_lines.sql \
      /tmp/complab_load_auxiliary.sql \
      /tmp/complab_runstats_before.sql \
      /tmp/complab_enable_compression.sql \
      /tmp/complab_reorg.sql

Documentación útil de IBM

Para profundizar, las referencias de IBM más útiles para este tema son:

Más en esta área

Más en esta área

Volver a formación
Categoría de artículos

Artículos de Db2 LUW

Consulta todos los artículos técnicos de Db2 LUW en una sola página de categoría.

Abrir categoría Db2 LUW