sqlpostgresqljsonbjson

Operadores flecha de JSONB: -> y ->> en PostgreSQL

Como funcionan -> y ->>: leer campos de objeto y elementos de array, descender por estructuras anidadas encadenando y convertir el resultado al tipo correcto.

2 min de lecturaReferencesql · postgresql · jsonb · json · mysql · clickhouse

Cuando guardas datos semiestructurados en una columna JSONB, toda lectura empieza con dos operadores: -> y ->>. Son la base de cualquier consulta JSON en PostgreSQL: uno desciende por la estructura y el otro extrae un valor listo para usar. Veamos en que se diferencian, como acceder a campos de objeto y elementos de array, y como encadenarlos.

-> devuelve jsonb, ->> devuelve text

Ambos operadores obtienen un valor por clave, pero devuelven tipos distintos. -> entrega jsonb (un objeto, un array o un escalar en forma JSON), mientras que ->> entrega text plano. Esa unica diferencia condiciona todo lo demas.

-- prefs is a jsonb column on users: {"theme": "dark", "lang": "en"}
SELECT
  prefs -> 'theme'  AS as_jsonb,   -- "dark"  (with quotes, type jsonb)
  prefs ->> 'theme' AS as_text     -- dark    (no quotes, type text)
FROM users;

Una regla sencilla:

  • Usa -> cuando vas a bajar mas y le seguira otro -> o ->>.
  • Usa ->> al final del camino, cuando quieres un valor escalar para mostrar o comparar.

La clave es una cadena (el nombre de un campo del objeto), y una clave inexistente devuelve NULL en vez de un error.

Campos de objeto y elementos de array

Si a la derecha del operador hay una cadena, PostgreSQL busca un campo del objeto. Si hay un entero, indexa un array, y la indexacion empieza en cero. Un indice negativo cuenta desde el final.

-- meta = {"tags": ["new", "vip", "eu"], "score": 7}
SELECT
  meta -> 'tags' -> 0     AS first_tag_json,   -- "new" as jsonb
  meta -> 'tags' ->> 0    AS first_tag_text,   -- new  as text
  meta -> 'tags' ->> -1   AS last_tag          -- eu   (last element)
FROM users;

Lo mismo aplica sobre tus propias tablas. Supongamos que orders tiene un payload con un array de lineas:

-- payload = {"items": [{"sku": "A1", "qty": 2}, {"sku": "B7", "qty": 1}]}
SELECT
  id,
  payload -> 'items' -> 0 ->> 'sku' AS first_sku
FROM orders
WHERE status = 'paid';

Encadenado y conversion de tipos

Un camino largo no es mas que varios operadores seguidos. Desciende con ->, manteniendote en jsonb, y en el ultimo paso cambia a ->> para obtener text. Como ->> siempre devuelve texto, convierte de forma explicita con :: para numeros, fechas y booleanos.

-- profile = {"address": {"city": "Berlin", "zip": "10115"}, "age": "34"}
SELECT
  profile -> 'address' ->> 'city'        AS city,
  (profile ->> 'age')::int               AS age_int
FROM users
WHERE (profile ->> 'age')::int >= 18;

Un ejemplo real de agregacion: extraer un importe de los metadatos JSON y calcular el ticket medio por pais.

-- payload = {"amount": "149.90", "currency": "EUR"}
SELECT
  u.country,
  round(avg((o.payload ->> 'amount')::numeric), 2) AS avg_amount
FROM orders o
JOIN users u ON u.id = o.user_id
GROUP BY u.country
ORDER BY avg_amount DESC;

Trampas habituales

  • Comparar sin convertir casi siempre es un error: payload ->> 'amount' > '99' compara cadenas lexicograficamente, asi que '9' queda por encima de '100'. Convierte a numero: (payload ->> 'amount')::numeric > 99.
  • -> sobre un escalar devuelve jsonb, asi que prefs -> 'theme' = 'dark' falla por error de tipos; usa ->> para comparar o escribe prefs -> 'theme' = '"dark"'::jsonb.
  • Una clave inexistente o un indice fuera de rango devuelve NULL en silencio, asi que un error tipografico en un nombre de campo no lanza error y corrompe el resultado sin avisar.
  • ->> sobre un objeto o un array devuelve su texto JSON completo, no un solo valor; toma texto solo sobre escalares.

MySQL no tiene este encadenado con flechas: usa JSON_EXTRACT(doc, '$.address.city') o el atajo doc->>'$.address.city' (su ->> tambien devuelve texto). ClickHouse ofrece funciones como JSONExtractString(col, 'city') y JSONExtractInt(col, 'age'). La idea es universal, pero la sintaxis de flechas -> / ->> es propia de PostgreSQL.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador