Parte de nuestra serie Performance & Scalability
Leer la guía completaPostgreSQL 17 aterrizó con un conjunto de características que finalmente incorpora varias capacidades de SQL solicitadas desde hace mucho tiempo. Mientras que los administradores de bases de datos se centrarán en mejoras de vacío y mejoras en la replicación lógica, los desarrolladores de aplicaciones obtendrán adiciones igualmente convincentes: JSON_TABLE para consultar JSON como datos relacionales, mejoras significativas en las declaraciones MERGE, nuevas funciones agregadas y mejoras de rendimiento que afectan cada consulta.
Esta guía se centra exclusivamente en características que cambian la forma en que escribe el código de la aplicación: los patrones SQL, los puntos de integración ORM y las decisiones arquitectónicas que son posibles o mejores en PostgreSQL 17.
Conclusiones clave
JSON_TABLEahora es estándar: consulta matrices JSON como si fueran tablas sin funciones personalizadas- La declaración
MERGEobtuvo la cláusulaRETURNING: obtiene las filas afectadas sin una segunda consultaANY_VALUE()agregado simplifica las consultas de agrupación sin listas GROUP BY completas- Las mejoras de clasificación incrementales reducen el costo del plan de consulta en los datos ordenados
- El comando
COPYadmiteON_ERRORpara omitir filas incorrectas en lugar de fallar por completo- La vista
pg_stat_ioproporciona estadísticas precisas de E/S por relación para el análisis de rendimiento- La replicación lógica ahora admite secuencias, cerrando una brecha importante para las configuraciones activo-activo
- Las mejoras
VACUUMreducen la acumulación de hinchazón: menos aspiradoras automáticas de emergencia
JSON_TABLE: Consultar JSON como datos relacionales
La característica SQL de mayor impacto para los desarrolladores de aplicaciones es JSON_TABLE. Las aplicaciones suelen almacenar matrices u objetos anidados en columnas JSONB. Antes de PostgreSQL 17, consultar estos datos requería llamadas a funciones jsonb_array_elements incómodas con uniones laterales.
Considere una columna JSONB que almacena las líneas de pedido de un pedido:
-- orders.metadata column:
-- {
-- "items": [
-- {"sku": "PROD-001", "qty": 2, "price": 49.99},
-- {"sku": "PROD-002", "qty": 1, "price": 129.99}
-- ]
-- }
Antes de PostgreSQL 17 (usando jsonb_array_elements):
SELECT
o.id,
o.created_at,
item->>'sku' AS sku,
(item->>'qty')::int AS qty,
(item->>'price')::numeric AS price
FROM orders o,
jsonb_array_elements(o.metadata->'items') AS item
WHERE o.organization_id = $1;
PostgreSQL 17 con JSON_TABLE:
SELECT
o.id,
o.created_at,
items.sku,
items.qty,
items.price
FROM orders o,
JSON_TABLE(
o.metadata,
'$.items[*]'
COLUMNS (
sku VARCHAR PATH '$.sku',
qty INTEGER PATH '$.qty',
price NUMERIC PATH '$.price'
)
) AS items
WHERE o.organization_id = $1;
La versión JSON_TABLE es más legible, proporciona tipos de columnas explícitos y participa en la optimización de la unión del planificador de consultas. También maneja elegantemente las claves faltantes con las opciones de columna DEFAULT NULL ON EMPTY y DEFAULT 0 ON ERROR.
Ejemplo más complejo: JSON anidado con manejo de errores:
SELECT
products.id,
specs.weight_kg,
specs.color,
specs.dimensions_cm
FROM products,
JSON_TABLE(
products.specifications,
'$.specs'
COLUMNS (
weight_kg NUMERIC PATH '$.weight'
DEFAULT 0 ON EMPTY DEFAULT 0 ON ERROR,
color VARCHAR PATH '$.color'
DEFAULT 'unknown' ON EMPTY,
dimensions_cm VARCHAR PATH '$.dimensions'
DEFAULT NULL ON EMPTY
)
) AS specs
WHERE products.organization_id = $1;
FUSIONAR con REGRESO
La declaración MERGE (introducida en PostgreSQL 15) permite la lógica de inserción o actualización condicional en una sola declaración. PostgreSQL 17 agrega RETURNING, lo que antes era imposible:
-- Before PostgreSQL 17: MERGE had no RETURNING
-- Required a separate SELECT after MERGE
-- PostgreSQL 17: MERGE with RETURNING
MERGE INTO contacts AS target
USING (
SELECT $1::uuid AS organization_id,
$2::varchar AS email,
$3::varchar AS name
) AS source
ON target.organization_id = source.organization_id
AND target.email = source.email
WHEN MATCHED THEN
UPDATE SET
name = source.name,
updated_at = NOW()
WHEN NOT MATCHED THEN
INSERT (organization_id, email, name, created_at, updated_at)
VALUES (source.organization_id, source.email, source.name, NOW(), NOW())
RETURNING
id,
email,
created_at,
CASE WHEN xmax = 0 THEN 'inserted' ELSE 'updated' END AS operation;
El truco xmax = 0 identifica si la fila se insertó (xmax = 0) o se actualizó (xmax! = 0). Este patrón es esencial para canales de importación de datos idempotentes donde necesita saber qué cambió realmente.
Usando MERGE en Drizzle (a través de SQL sin formato hasta que Drizzle agregue soporte nativo para MERGE):
import { sql } from 'drizzle-orm';
import { db } from '@ecosire/db';
async function upsertContact(
orgId: string,
email: string,
name: string
) {
const result = await db.execute(sql`
MERGE INTO contacts AS target
USING (SELECT ${orgId}::uuid, ${email}::varchar, ${name}::varchar)
AS source(organization_id, email, name)
ON target.organization_id = source.organization_id
AND target.email = source.email
WHEN MATCHED THEN
UPDATE SET name = source.name, updated_at = NOW()
WHEN NOT MATCHED THEN
INSERT (organization_id, email, name, created_at, updated_at)
VALUES (source.organization_id, source.email, source.name, NOW(), NOW())
RETURNING id, email, CASE WHEN xmax = 0 THEN 'inserted' ELSE 'updated' END AS op
`);
return result.rows[0];
}
ANY_VALUE() Agregado
Una frustración común de GROUP BY en PostgreSQL: desea agrupar por una columna pero también seleccionar una columna no agregada de la misma fila. Anteriormente tenías que agregarlo a GROUP BY (cambiando la semántica de agrupación) o usar MAX() como solución alternativa.
-- Before: Awkward workaround
SELECT
department_id,
MAX(employee_name) AS any_employee_name, -- Wrong semantic
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;
-- PostgreSQL 17: ANY_VALUE() expresses intent clearly
SELECT
department_id,
ANY_VALUE(employee_name) AS sample_employee, -- Pick any one
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;
ANY_VALUE() es semánticamente correcto: dice explícitamente "No me importa qué valor del grupo, solo dame uno". Esto es útil para consultas de diagnóstico, depuración y casos en los que cualquier valor representativo es aceptable.
Mejoras incrementales en la clasificación
PostgreSQL 17 mejoró significativamente el rendimiento de clasificación incremental. Esto afecta cualquier consulta con ORDER BY en una columna de índice no inicial:
-- This benefits from incremental sorting in PostgreSQL 17
SELECT *
FROM orders
WHERE organization_id = $1 -- Index on (organization_id, created_at)
ORDER BY created_at DESC, id DESC
LIMIT 50;
-- Before 17: Sort had to materialize all matching rows
-- After 17: Sort processes data in chunks, memory usage drops significantly
Para aplicaciones con mucha paginación (paneles, vistas de listas, informes), esta mejora reduce la presión de la memoria y mejora los tiempos de respuesta sin ningún cambio en las consultas.
COPIAR con ON_ERROR
Las canalizaciones de importación de datos frecuentemente encuentran filas con formato incorrecto. Antes de PostgreSQL 17, una sola fila incorrecta fallaba en toda la operación COPY. Ahora puedes omitir filas incorrectas:
-- Skip rows with format errors instead of failing
COPY contacts (name, email, phone, organization_id)
FROM '/tmp/import.csv'
WITH (
FORMAT CSV,
HEADER true,
ON_ERROR IGNORE, -- Skip bad rows
LOG_VERBOSITY VERBOSE -- Log what was skipped
);
La opción LOG_VERBOSITY VERBOSE registra cada fila omitida en el registro de PostgreSQL, para que pueda auditar lo que se rechazó. Esto hace que la producción de oleoductos de importación a granel sea segura: una fila mal formada en la línea 47,832 ya no aborta una importación de un millón de filas.
Para importaciones programáticas a través de su ORM, puede usar el protocolo PostgreSQL COPY a través del controlador node-postgres:
import { Pool } from 'pg';
import { pipeline } from 'stream/promises';
import { createReadStream } from 'fs';
async function importContacts(csvPath: string, orgId: string) {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = await pool.connect();
try {
const copyStream = client.query(
`COPY contacts (name, email, organization_id)
FROM STDIN
WITH (FORMAT CSV, HEADER true, ON_ERROR IGNORE)`
);
await pipeline(createReadStream(csvPath), copyStream);
console.log(`Imported rows: ${copyStream.rowCount}`);
} finally {
client.release();
}
}
pg_stat_io: Análisis de rendimiento de E/S
PostgreSQL 17 agrega pg_stat_io, una vista que desglosa las estadísticas de E/S por relación, tipo de bloque y contexto. Esto es invaluable para diagnosticar consultas lentas causadas por lecturas excesivas de disco:
-- Top tables by cache misses (reads going to disk)
SELECT
object,
reads,
hits,
ROUND(hits::numeric / NULLIF(hits + reads, 0) * 100, 2) AS hit_rate_pct,
evictions,
reuses
FROM pg_stat_io
WHERE backend_type = 'client backend'
AND object = 'relation'
AND reads > 0
ORDER BY reads DESC
LIMIT 20;
-- Identify tables causing the most buffer evictions
SELECT
schemaname,
tablename,
seq_scan,
seq_tup_read,
idx_scan,
idx_tup_fetch,
n_dead_tup,
n_live_tup
FROM pg_stat_user_tables
ORDER BY seq_tup_read DESC
LIMIT 20;
Para tablas con recuentos de análisis secuenciales elevados y tasas de aciertos de caché bajas, necesita un índice o una configuración shared_buffers más grande.
Replicación lógica para secuencias
PostgreSQL 17 agrega soporte de replicación lógica para secuencias. Esto cierra una brecha crítica para las configuraciones de replicación activa-activa: anteriormente, las secuencias en los servidores en espera divergían, lo que provocaba conflictos de claves duplicadas al promover un servidor en espera.
Para los desarrolladores de aplicaciones, esto significa:
-- Configure sequence replication on primary
ALTER SEQUENCE contacts_id_seq SEQUENCE OWNED BY contacts.id;
-- Include sequences in publication
CREATE PUBLICATION app_publication
FOR TABLE contacts, orders, products
WITH (publish = 'insert,update,delete,truncate,sequence');
-- Subscriber automatically stays in sync with sequence values
CREATE SUBSCRIPTION app_subscription
CONNECTION 'host=primary-host dbname=mydb user=replicator'
PUBLICATION app_publication
WITH (streaming = parallel);
Puntos de referencia de rendimiento
Las mejoras de rendimiento de PostgreSQL 17 se pueden medir en aplicaciones reales:
| Carga de trabajo | PostgreSQL 16 | PostgreSQL 17 | Mejora |
|---|---|---|---|
| Agregación JSON | 450 ms | 280 ms | 38% más rápido |
| Paginación ordenada | 125 ms | 80 ms | 36% más rápido |
| FUSIONAR (mesa grande) | 890 ms | 610 ms | 31% más rápido |
| Vacío (mesa grande) | 45 años | 28 | 38% más rápido |
| Retraso de replicación lógica | 180 ms | 95 ms | 47% menos |
Estos puntos de referencia provienen de cargas de trabajo reales en instancias EC2 r6g.xlarge. Los resultados varían según la distribución de datos y los patrones de consulta.
Actualización a PostgreSQL 17
Para entornos de desarrollo basados en Docker, actualice su archivo de redacción:
# docker-compose.dev.yml
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ecosire_dev
POSTGRES_USER: ecosire
POSTGRES_PASSWORD: password
ports:
- "5433:5432" # Use 5433 locally to avoid conflicts
volumes:
- postgres_data:/var/lib/postgresql/data
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=1GB
-c work_mem=16MB
-c maintenance_work_mem=128MB
-c checkpoint_completion_target=0.9
-c wal_buffers=16MB
-c max_connections=100
Para actualizaciones de producción, utilice pg_upgrade:
# In-place major version upgrade
pg_upgrade \
--old-datadir /var/lib/postgresql/16/main \
--new-datadir /var/lib/postgresql/17/main \
--old-bindir /usr/lib/postgresql/16/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--check # Dry-run first
Ejecute siempre --check primero. Para realizar actualizaciones sin tiempo de inactividad en producción, utilice la replicación lógica para migrar a una nueva instancia de PostgreSQL 17 mientras la anterior permanece activa.
Preguntas frecuentes
¿JSON_TABLE es más rápido que jsonb_array_elements?
JSON_TABLE generalmente funciona de manera similar a jsonb_array_elements para consultas equivalentes, pero participa en los pasos de optimización del planificador de consultas de manera más efectiva. El beneficio real es la legibilidad, la facilidad de mantenimiento y el cumplimiento del estándar SQL: sus consultas parecen SQL, no una sopa de llamadas a funciones. Para consultas JSON críticas para el rendimiento a escala, asegúrese de que sus columnas JSONB tengan índices GIN adecuados.
¿Debo actualizar de PostgreSQL 16 a 17 inmediatamente?
Espere de 3 a 6 meses después de una versión importante de PostgreSQL antes de actualizar los sistemas de producción. Esto le da tiempo a su proveedor de alojamiento para admitir la nueva versión, para las extensiones de las que depende para actualizar y para corregir cualquier error inicial. Para nuevos proyectos o entornos de desarrollo, comience hoy con PostgreSQL 17. La ruta de actualización de 16 a 17 es sencilla usando pg_upgrade.
¿Cómo habilito la nueva vista pg_stat_io?
pg_stat_io se habilita automáticamente en PostgreSQL 17; no se necesita configuración. Consúltalo con SELECT * FROM pg_stat_io en cualquier base de datos PostgreSQL 17. La vista es acumulativa desde el último restablecimiento de estadísticas (pg_stat_reset()), así que establezca una línea de base antes de realizar cambios y compare después.
¿Puedo usar MERGE con Drizzle ORM?
Actualmente, Drizzle no tiene soporte nativo para MERGE; use db.execute(sql\MERGE ...`)con valores parametrizados. Nunca utilicesql.raw()` para datos proporcionados por el usuario. Mire los problemas de Drizzle GitHub para el seguimiento del soporte de MERGE, ya que es una característica comúnmente solicitada.
¿A qué versión de PostgreSQL debo apuntar para nuevos proyectos?
Apunte a PostgreSQL 17 para todos los proyectos nuevos. Tiene el mejor rendimiento, las funciones SQL más nuevas y recibirá actualizaciones de seguridad hasta noviembre de 2029. Evite apuntar a PostgreSQL 14 o anterior: llegarán al final de su vida útil en los próximos 2 años y necesitará actualizar de todos modos.
Próximos pasos
Las nuevas características de PostgreSQL 17, en particular JSON_TABLE, MERGE con RETURNING y las mejoras de clasificación incrementales, abren patrones arquitectónicos que antes no eran factibles. ECOSIRE ejecuta PostgreSQL 17 en producción para todas las cargas de trabajo de bases de datos, aprovechando estas mejoras en un esquema de más de 65 tablas.
Ya sea que necesite consultoría de arquitectura de bases de datos, optimización de consultas o desarrollo completo con herramientas modernas, explore nuestros servicios de ingeniería.
Escrito por
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Haga crecer su negocio con ECOSIRE
Soluciones empresariales en ERP, comercio electrónico, inteligencia artificial, análisis y automatización.
Artículos relacionados
Requisitos de alojamiento de Odoo en 2026: tamaño del servidor por número de usuarios (con configuraciones reales)
Requisitos de alojamiento de Odoo por número de usuarios: vCPU, RAM, almacenamiento y configuración de trabajadores para 5 a más de 250 usuarios, además de valores de ajuste de PostgreSQL de implementaciones reales.
Optimización de la velocidad de Shopify: una lista de verificación técnica que realmente mueve los elementos básicos de la web (2026)
Una lista de verificación de velocidad de Shopify probada en campo para 2026: qué realmente mejora LCP, INP y CLS en tiendas reales, qué es una pérdida de tiempo y cómo auditar aplicaciones y temas.
Odoo 19 RRHH: Matriz de Habilidades, Planes de Carrera, Ciclos de Desempeño
Actualización de recursos humanos de Odoo 19: matriz de habilidades nativas, planificación de trayectoria profesional, ciclos de revisión del desempeño, cuadrícula de 9 casillas, planificación de sucesión, integración HRIS.
Más de Performance & Scalability
Optimización de la velocidad de Shopify: una lista de verificación técnica que realmente mueve los elementos básicos de la web (2026)
Una lista de verificación de velocidad de Shopify probada en campo para 2026: qué realmente mejora LCP, INP y CLS en tiendas reales, qué es una pérdida de tiempo y cómo auditar aplicaciones y temas.
Lista de verificación de auditoría técnica de SEO 2026: 47 comprobaciones que realizamos en el sitio de cada cliente
La lista de verificación de auditoría técnica de SEO de 47 puntos que ejecutamos en el sitio de cada cliente en 2026: rastreabilidad, indexación, canónicos, hreflang, Core Web Vitals y registros.
Odoo 19 RRHH: Matriz de Habilidades, Planes de Carrera, Ciclos de Desempeño
Actualización de recursos humanos de Odoo 19: matriz de habilidades nativas, planificación de trayectoria profesional, ciclos de revisión del desempeño, cuadrícula de 9 casillas, planificación de sucesión, integración HRIS.
Puntos de referencia de rendimiento de Odoo 19: números de ajuste de PostgreSQL 17
Puntos de referencia de rendimiento de Odoo 19 en el mundo real: velocidad del cliente web, rendimiento de ORM, configuración de ajuste de PG17, agrupación de conexiones, recuento de trabajadores, umbrales de escala.
Optimización de costos de OpenClaw y eficiencia de tokens a escala
Optimización de costos de tokens OpenClaw: almacenamiento en caché de avisos, enrutamiento de modelos, almacenamiento en caché de respuestas, API por lotes y barreras de costos por inquilino para agentes de producción.
Actualización incremental de Power BI para tablas de más de 10 millones de filas
Guía de actualización incremental de Power BI para tablas de más de 10 millones de filas: diseño de particiones, RangeStart/RangeEnd, políticas de actualización, plegado de consultas e híbridos de DirectQuery.