PostgreSQL'de iç içe JSON belgelerinden veri çekmenin standart yolu SQL/JSON'dur: $.kalemler[*] ? (@.adet >= 5).urun gibi bir jsonpath ifadesi belgede gezinip filtre uygular, JSON_VALUE tek bir skaler değeri SQL tipine çevirir, JSON_QUERY nesne ya da dizi döndürür, JSON_EXISTS bir yolun eşleşip eşleşmediğini söyler, JSON_TABLE ise JSON dizisini doğrudan satır ve sütunlara açar. Ayrıca jsonb_path_query ailesi ve @?, @@ operatörleri aynı yol dilini kullanır.

Bu yazı jsonpath sözdizimini, filtre ifadelerini, lax ve strict modlarını, değişken geçirmeyi, SQL/JSON sorgu fonksiyonlarının ON EMPTY ve ON ERROR davranışlarını, JSON_TABLE ile NESTED PATH kullanımını ve IS JSON doğrulamasını anlatır. ->, ->>, @> operatörleri, jsonb_set ve GIN indeks gibi temel jsonb konuları PostgreSQL JSONB kullanımı yazısında. Örnekler PostgreSQL 18.1 üzerinde çalıştırıldı; serinin geri kalanı için PostgreSQL öğrenme rehberi.

Örnek veri

Her siparişin müşteri, şehir, tarih, kalem listesi ve isteğe bağlı kargo bilgisini tek bir jsonb belgesinde tuttuğumuzu varsayalım. 2 numaralı siparişte kargo bilgisi yok, not alanı ise JSON null:

sql
CREATE TABLE siparis (id int PRIMARY KEY, belge jsonb NOT NULL);
INSERT INTO siparis VALUES
 (1, '{"musteri": "Ayşe Yılmaz", "sehir": "İstanbul", "tarih": "2026-03-15",
       "kalemler": [{"urun": "Kalem", "adet": 10, "fiyat": 12.5},
                    {"urun": "Defter", "adet": 3, "fiyat": 45}],
       "kargo": {"firma": "Hızlı", "ucret": 0}}'),
 (2, '{"musteri": "Mehmet Kaya", "sehir": "Ankara", "tarih": "2026-03-16",
       "kalemler": [{"urun": "Çanta", "adet": 1, "fiyat": 850}],
       "not": null}'),
 (3, '{"musteri": "Zeynep Demir", "sehir": "İzmir", "tarih": "2026-03-18",
       "kalemler": [{"urun": "Kalem", "adet": 2, "fiyat": 12.5},
                    {"urun": "Silgi", "adet": 5, "fiyat": 4}],
       "kargo": {"firma": "Ekonomik", "ucret": 39.9}}');

jsonpath dilinin temelleri

Bir yol ifadesi her zaman sorgulanan belgeyi temsil eden $ ile başlar. Ardından gelen erişimciler belgede adım adım aşağı iner: .anahtar nesne üyesini, [n] dizi elemanını, [*] dizinin bütün elemanlarını seçer. SQL dizilerinden farklı olarak JSON dizileri sıfırdan başlar. Anahtar adı boşluk içeriyorsa ya da JavaScript tanımlayıcı kurallarına uymuyorsa çift tırnak içine alınır, örneğin $."baslangic zamani". Yol ifadesi SQL içinde tek tırnaklı bir metin olarak yazıldığı için içerideki metin sabitleri çift tırnak kullanır.

Her adım sıfır, bir ya da daha çok sonuç üretebilir. Aşağıdaki sorguda [*] iki kalemi ayrı ayrı seçti, .urun de her birinin ürün adını aldı; jsonb_path_query küme döndüren bir fonksiyon olduğu için iki satır çıktı:

sql
SELECT jsonb_path_query(belge, '$.kalemler[*].urun') AS urun FROM siparis WHERE id = 1;
text
   urun   
----------
 "Kalem"
 "Defter"
(2 rows)

Satır sayısını değiştirmek istemiyorsanız sonuçları bir dizide toplayan jsonb_path_query_array ya da yalnızca ilk sonucu döndüren jsonb_path_query_first kullanın. Yolun sonuna nokta ile yöntem eklenebilir: .size() dizinin eleman sayısını, .type() değerin tipini, .floor(), .double(), .datetime() gibi yöntemler dönüşüm verir:

sql
SELECT id, jsonb_path_query_array(belge, '$.kalemler[*].urun') AS urunler,
       jsonb_path_query_first(belge, '$.kalemler[*].fiyat') AS ilk_fiyat,
       jsonb_path_query(belge, '$.kalemler.size()') AS kalem_sayisi
FROM siparis ORDER BY id;
text
 id |       urunler       | ilk_fiyat | kalem_sayisi 
----+---------------------+-----------+--------------
  1 | ["Kalem", "Defter"] | 12.5      | 2
  2 | ["Çanta"]           | 850       | 1
  3 | ["Kalem", "Silgi"]  | 12.5      | 2
(3 rows)

Bir metni jsonpath tipine çevirdiğinizde PostgreSQL onu ayrıştırıp normalleştirilmiş biçimde gösterir. Bu, yazdığınız yolun nasıl anlaşıldığını görmenin kolay bir yoludur:

sql
SELECT '$.kalemler[*] ? (@.adet > 2)'::jsonpath AS yol;
text
              yol               
--------------------------------
 $."kalemler"[*]?(@."adet" > 2)
(1 row)

Filtre ifadeleri: ? (koşul)

Filtre ifadeleri: ? (koşul): Metin filtreleri: like_regex ve starts with, Değişken geçirmek

Filtre, SQL'deki WHERE gibi çalışır ve ? (koşul) biçiminde, uygulanacağı adımın hemen arkasına yazılır. Koşulun içinde @ değişkeni o anda değerlendirilen öğeyi temsil eder. Karşılaştırma için ==, !=, <, >= gibi operatörler, mantık için &&, || ve ! kullanılır. Filtrenin hangi adıma uygulandığı sonucu belirler: aşağıda filtre kalem nesnelerine uygulanıyor, sonra filtreden geçen kalemlerin ürün adı alınıyor:

sql
SELECT id, jsonb_path_query(belge, '$.kalemler[*] ? (@.adet >= 5).urun') AS cok_adet
FROM siparis ORDER BY id;
text
 id | cok_adet 
----+----------
  1 | "Kalem"
  3 | "Silgi"
(2 rows)

2 numaralı sipariş sonuçta yok: jsonb_path_query eşleşme olmayan belge için satır üretmez. Bu, JSONB yazısında gördüğümüz küme döndüren fonksiyonların davranışıyla aynıdır.

Metin filtreleri: like_regex ve starts with

Filtre içinde metin aramak için iki yüklem vardır: like_regex bir düzenli ifadeyle eşleşmeyi, isteğe bağlı flag "i" ile harfe duyarsız olarak sınar; starts with ön ek kontrolü yapar. Belgeler like_regex desenlerinin yorumlanmasında SQL'deki POSIX düzenli ifadelerinden küçük farklar olduğunu belirtir.

sql
SELECT id, jsonb_path_query_array(belge, '$.kalemler[*] ? (@.urun like_regex "^ka" flag "i").urun') AS regex,
       jsonb_path_query_array(belge, '$.kalemler[*] ? (@.urun starts with "S").urun') AS baslar
FROM siparis ORDER BY id;
text
 id |   regex   |  baslar   
----+-----------+-----------
  1 | ["Kalem"] | []
  2 | []        | []
  3 | ["Kalem"] | ["Silgi"]
(3 rows)

Değişken geçirmek

Yol içinde $ad biçiminde adlandırılmış değişkenler kullanabilirsiniz. jsonb_path_* fonksiyonlarında değerler üçüncü argüman olarak bir JSON nesnesiyle, SQL/JSON fonksiyonlarında ise PASSING deger AS ad ile verilir. Uygulamadan gelen sınır değerlerini yol metnine birleştirmek yerine bu yolu kullanın:

sql
SELECT id, jsonb_path_exists(belge, '$.kalemler[*] ? (@.fiyat > $alt && @.fiyat < $ust)', '{"alt": 10, "ust": 50}') AS aralikta
FROM siparis ORDER BY id;
text
 id | aralikta 
----+----------
  1 | t
  2 | f
  3 | t
(3 rows)

@? ve @@ operatörleri

WHERE içinde jsonpath kullanmanın iki kısa yolu vardır. @? "yol bir sonuç üretiyor mu?" sorusunu yanıtlar ve filtreli standart yol ifadeleriyle kullanılır. @@ ise bir doğruluk yüklemi bekler; PostgreSQL'in standarda eklediği bu biçimde yolun kendisi $.kalemler[*].fiyat > 500 gibi bir karşılaştırmadır. Belgeler iki biçimin karıştırılmamasını özellikle vurgular: yüklem ifadesi her zaman bir değer döndürdüğü için @? ile anlamsızdır, standart yol ise @@ ile tek bir mantıksal değer üretmediğinde NULL verir.

sql
SELECT id FROM siparis WHERE belge @? '$.kalemler[*] ? (@.urun == "Kalem")' ORDER BY id;
SELECT id FROM siparis WHERE belge @@ '$.kalemler[*].fiyat > 500';
text
 id 
----
  1
  3
(2 rows)

 id 
----
  2
(1 row)

Bu iki operatör jsonb sütunundaki GIN indeksini kullanabilir. Belgelere göre GIN, yol ifadesinden erisim_zinciri == sabit biçimindeki koşulları çıkarıp indeks aramasını onlara göre yapar; dolayısıyla eşitlik içeren filtreler indeksten en çok yararlanan sorgulardır.

lax ve strict modları

Yol belgenin gerçek yapısına uymadığında, örneğin dizi olan bir değere nesne erişimcisi uygulandığında bu bir "yapısal hata"dır. Varsayılan lax modunda motor veriyi yola uydurmaya çalışır: dizileri gerektiğinde otomatik olarak açar ya da tek değeri diziye sarar, düzeltemediği hataları sessizce eşleşmesiz sayar. strict modunda ise yapısal hata gerçek bir hataya dönüşür. Yolun başına lax ya da strict yazarak modu seçersiniz:

sql
SELECT jsonb_path_query(belge, 'lax $.kalemler.urun') AS lax_mod FROM siparis WHERE id = 1;
SELECT jsonb_path_query(belge, 'strict $.kalemler.urun') AS strict_mod FROM siparis WHERE id = 1;
text
 lax_mod  
----------
 "Kalem"
 "Defter"
(2 rows)

ERROR:  jsonpath member accessor can only be applied to an object

Lax modunda kalemler dizisi kendiliğinden açıldı ve [*] yazmadan da ürünler geldi. Strict modda aynı sonucu almak için diziyi [*] ile açıkça açmanız gerekir. Lax modun bu kolaylığı sürprizlere de yol açabilir; belgeler .** (her seviyeye inen) erişimcisinin lax modda değerleri iki kez seçebildiğini gösterir ve bu erişimcinin yalnızca strict modda kullanılmasını önerir.

Hataları susturmanın bir yolu da jsonb_path_* fonksiyonlarının silent parametresidir. Doğru verildiğinde eksik alan, beklenmeyen tip, tarih ve sayı hataları bastırılır; bu, @? ve @@ operatörlerinin zaten yaptığı şeydir. Kargo bilgisi olmayan 2 numaralı siparişte strict yol hata vermek yerine NULL döndürdü:

sql
SELECT id, jsonb_path_query_first(belge, 'strict $.kargo.firma', silent => true) AS sessiz FROM siparis ORDER BY id;
text
 id |   sessiz   
----+------------
  1 | "Hızlı"
  2 | 
  3 | "Ekonomik"
(3 rows)

Tarihlerle çalışmak: .datetime()

JSON'da tarih diye bir tip yoktur, tarihler metin olarak gelir. .datetime() yöntemi bir metni tarih/saat değerine çevirir ve böylece yol içinde tarih karşılaştırması yapılabilir. İsteğe bağlı bir şablon argümanı alır; belgelere göre ayrıştırma to_timestamp fonksiyonunun kurallarıyla yapılır, yalnızca eşleşmeyen kalıplara izin verilmemesi ve kabul edilen ayırıcıların sınırlı olması gibi birkaç istisna vardır. Saat dilimine bağlı dönüşüm gerektiren karşılaştırmalar için fonksiyonların _tz ile biten sürümleri (jsonb_path_exists_tz gibi) vardır.

sql
SELECT id, jsonb_path_query_first(belge, '$.tarih.datetime() ? (@ >= "2026-03-16".datetime())') AS sonra
FROM siparis ORDER BY id;
text
 id |    sonra     
----+--------------
  1 | 
  2 | "2026-03-16"
  3 | "2026-03-18"
(3 rows)

Tarih tipleri ve şablon kalıpları PostgreSQL tarih ve saat yazısında ayrıntılı olarak anlatılıyor.

JSON_VALUE, JSON_QUERY ve JSON_EXISTS

JSON_VALUE, JSON_QUERY ve JSON_EXISTS: Sessiz NULL tuzağı, JSON_QUERY ve WRAPPER, QUOTES

SQL standardındaki üç sorgu fonksiyonu bir belge ve bir yol alır, değişkenleri PASSING ile kabul eder ve boş sonuç ile hata durumunda ne yapılacağını ON EMPTY ve ON ERROR yan tümceleriyle belirlemenizi sağlar.

FonksiyonDöndürdüğüVarsayılan ON EMPTYVarsayılan ON ERROR
JSON_EXISTSbooleanuygulanmazFALSE
JSON_VALUEtek skaler, varsayılan textNULLNULL
JSON_QUERYnesne, dizi ya da skaler, varsayılan jsonbNULLNULL

JSON_VALUE tek bir skaler değer bekler ve RETURNING ile istediğiniz SQL tipine çevirir; metin değerlerin tırnakları her zaman kaldırılır. DEFAULT ... ON EMPTY eksik alanlar için yedek değer verir:

sql
SELECT id,
       JSON_VALUE(belge, '$.musteri') AS musteri,
       JSON_VALUE(belge, '$.kargo.ucret' RETURNING numeric DEFAULT 0 ON EMPTY) AS kargo_ucret,
       JSON_VALUE(belge, '$.tarih' RETURNING date) AS tarih,
       JSON_EXISTS(belge, '$.kargo') AS kargolu
FROM siparis ORDER BY id;
text
 id |   musteri    | kargo_ucret |   tarih    | kargolu 
----+--------------+-------------+------------+---------
  1 | Ayşe Yılmaz  |           0 | 2026-03-15 | t
  2 | Mehmet Kaya  |           0 | 2026-03-16 | f
  3 | Zeynep Demir |        39.9 | 2026-03-18 | t
(3 rows)

1 numaralı siparişteki 0 gerçek değerdir, 2 numaralıdaki 0 ise DEFAULT ile gelen yedek değerdir; ikisini ayırt etmeniz gerekiyorsa JSON_EXISTS sütununa bakın ya da yedek değer kullanmayın.

Sessiz NULL tuzağı

Bu fonksiyonların en çok şaşırtan yanı, varsayılan NULL ON ERROR davranışıdır: bir hata oluştuğunda size hata yerine NULL döner. Örneğin JSON_VALUE birden çok değer üreten bir yolla çağrılırsa sonuç sessizce NULL olur. Geliştirme sırasında ERROR ON ERROR ekleyerek gerçek nedeni görebilirsiniz:

sql
SELECT JSON_VALUE(belge, '$.kalemler[*].urun') AS coklu_sessiz FROM siparis WHERE id = 1;
SELECT JSON_VALUE(belge, '$.kalemler[*].urun' ERROR ON ERROR) FROM siparis WHERE id = 1;
text
 coklu_sessiz 
--------------
 
(1 row)

ERROR:  JSON path expression in JSON_VALUE must return single scalar item

JSON_QUERY ve WRAPPER, QUOTES

JSON_QUERY nesne ya da dizi döndürmek için kullanılır. Yol birden çok değer üretiyorsa onları geçerli bir JSON dizisine sarmak için WITH WRAPPER gerekir; WITH CONDITIONAL WRAPPER yalnızca birden çok değer olduğunda sarar. Skaler metin sonuçlar varsayılan olarak tırnaklı döner (KEEP QUOTES); OMIT QUOTES tırnakları kaldırır:

sql
SELECT id,
       JSON_QUERY(belge, '$.kargo') AS kargo,
       JSON_QUERY(belge, '$.kalemler[*].urun' WITH WRAPPER) AS urunler,
       JSON_QUERY(belge, '$.kalemler[*].urun' WITH CONDITIONAL WRAPPER) AS kosullu,
       JSON_QUERY(belge, '$.musteri') AS tirnakli,
       JSON_QUERY(belge, '$.musteri' OMIT QUOTES) AS tirnaksiz
FROM siparis ORDER BY id;
text
 id |                kargo                 |       urunler       |       kosullu       |    tirnakli    | tirnaksiz 
----+--------------------------------------+---------------------+---------------------+----------------+-----------
  1 | {"firma": "Hızlı", "ucret": 0}       | ["Kalem", "Defter"] | ["Kalem", "Defter"] | "Ayşe Yılmaz"  | 
  2 |                                      | ["Çanta"]           | "Çanta"             | "Mehmet Kaya"  | 
  3 | {"firma": "Ekonomik", "ucret": 39.9} | ["Kalem", "Silgi"]  | ["Kalem", "Silgi"]  | "Zeynep Demir" | 
(3 rows)

Son sütun boş geldi; bu yine sessiz NULL tuzağıdır. JSON_QUERY varsayılan olarak jsonb döndürür, tırnakları atılmış Ayşe Yılmaz ise geçerli bir JSON değeri değildir; dönüşüm hatası NULL ON ERROR ile NULL'a çevrildi. Tırnaksız metin istiyorsanız dönüş tipini metin yapın ya da doğrudan JSON_VALUE kullanın:

sql
SELECT JSON_QUERY(jsonb '{"musteri": "Ayşe Yılmaz"}', '$.musteri' OMIT QUOTES) AS jsonb_donus,
       JSON_QUERY(jsonb '{"musteri": "Ayşe Yılmaz"}', '$.musteri' RETURNING text OMIT QUOTES) AS text_donus;
text
 jsonb_donus | text_donus  
-------------+-------------
             | Ayşe Yılmaz
(1 row)

Belgelerdeki bir notu da unutmayın: yol JSON null ürettiğinde JSON_VALUE SQL NULL döndürür, JSON_QUERY ise JSON null değerini olduğu gibi verir. 2 numaralı siparişteki not alanında ilk sütun boş, ikinci sütun null çıktı. Ayrıca bağlam öğesi metin olarak verilip jsonb'ye çevrilirken oluşan ayrıştırma hataları ON ERROR yan tümcesinden bağımsız olarak her zaman hata olarak bildirilir.

sql
SELECT JSON_VALUE(belge, '$.not') AS value_null, JSON_QUERY(belge, '$.not') AS query_null
FROM siparis WHERE id = 2;

SELECT JSON_VALUE(belge, '$.kalemler[0].adet' RETURNING int) * 2 AS iki_kat,
       JSON_EXISTS(belge, 'strict $.kalemler[5]') AS sessiz_false,
       JSON_EXISTS(belge, 'strict $.kalemler[*] ? (@.fiyat > $f)' PASSING 40 AS f) AS passing_ile
FROM siparis WHERE id = 1;
text
 value_null | query_null 
------------+------------
            | null
(1 row)

 iki_kat | sessiz_false | passing_ile 
---------+--------------+-------------
      20 | f            | t
(1 row)

İkinci sorguda strict modda sınır dışı bir dizi indeksi hata üretti, ama JSON_EXISTS varsayılan FALSE ON ERROR ile bunu yanlış olarak raporladı. Hatayı görmek isteseydiniz ERROR ON ERROR yazmanız gerekirdi.

JSON_TABLE: JSON dizisini tabloya çevirmek

JSON_TABLE, FROM içinde kullanılan ve JSON verisini gerçek bir tablo gibi sunan fonksiyondur. İlk yol ifadesi satır desenini belirler: o yolun ürettiği her değer bir satır olur. COLUMNS yan tümcesi her sütun için tipi ve satır desenine göre göreli bir yolu tanımlar. FOR ORDINALITY 1'den başlayan sıra numarası, EXISTS PATH ise bir yolun var olup olmadığını gösteren mantıksal sütun üretir. Üretilen satırlar kaynak satıra yanal (lateral) olarak bağlandığı için ayrıca JOIN koşulu yazmanız gerekmez:

sql
SELECT s.id, jt.*
FROM siparis s,
     JSON_TABLE(s.belge, '$.kalemler[*]'
       COLUMNS (
         sira   FOR ORDINALITY,
         urun   text    PATH '$.urun',
         adet   int     PATH '$.adet',
         fiyat  numeric PATH '$.fiyat',
         indirimli boolean EXISTS PATH '$.indirim'
       )) AS jt
ORDER BY s.id, jt.sira;
text
 id | sira |  urun  | adet | fiyat | indirimli 
----+------+--------+------+-------+-----------
  1 |    1 | Kalem  |   10 |  12.5 | f
  1 |    2 | Defter |    3 |    45 | f
  2 |    1 | Çanta  |    1 |   850 | f
  3 |    1 | Kalem  |    2 |  12.5 | f
  3 |    2 | Silgi  |    5 |     4 | f
(5 rows)

Bu noktadan sonra JSON verisi sıradan bir tablodur: üzerinde gruplama, sıralama ve birleştirme yapabilirsiniz. Ürün bazında ciro raporu tek sorguyla çıkar:

sql
SELECT jt.urun, sum(jt.adet) AS toplam_adet, sum(jt.adet * jt.fiyat) AS ciro
FROM siparis s,
     JSON_TABLE(s.belge, '$.kalemler[*]' COLUMNS (urun text PATH '$.urun', adet int PATH '$.adet', fiyat numeric PATH '$.fiyat')) AS jt
GROUP BY jt.urun ORDER BY ciro DESC;
text
  urun  | toplam_adet | ciro  
--------+-------------+-------
 Çanta  |           1 |   850
 Kalem  |          12 | 150.0
 Defter |           3 |   135
 Silgi  |           5 |    20
(4 rows)

NESTED PATH ile iç içe seviyeler

Üst seviye alanları ve iç içe bir diziyi aynı sorguda almak için NESTED PATH kullanılır. İç içe yan tümcenin ürettiği "çocuk satırlar" üst satırla birleştirilir. PATH yazmazsanız sütun adı yol olarak kabul edilir (aşağıdaki sehir sütunu $.sehir yolunu kullanıyor). Sütun yolları aritmetik de içerebilir:

sql
SELECT jt.*
FROM siparis s,
     JSON_TABLE(s.belge, '$'
       COLUMNS (
         musteri text PATH '$.musteri',
         sehir   text,
         kargo_firma text PATH '$.kargo.firma' DEFAULT 'yok' ON EMPTY,
         NESTED PATH '$.kalemler[*]' COLUMNS (
           urun text PATH '$.urun',
           tutar numeric PATH '$.adet * $.fiyat'
         )
       )) AS jt
ORDER BY musteri, urun;
text
   musteri    |  sehir   | kargo_firma |  urun  | tutar 
--------------+----------+-------------+--------+-------
 Ayşe Yılmaz  | İstanbul | Hızlı       | Defter |   135
 Ayşe Yılmaz  | İstanbul | Hızlı       | Kalem  | 125.0
 Mehmet Kaya  | Ankara   | yok         | Çanta  |   850
 Zeynep Demir | İzmir    | Ekonomik    | Kalem  |  25.0
 Zeynep Demir | İzmir    | Ekonomik    | Silgi  |    20
(5 rows)

Aynı seviyede birden çok NESTED PATH yazarsanız belgelere göre bunlar "kardeş" sayılır ve satırları UNION ile birleştirilir, yani birbirleriyle çapraz çarpılmazlar. Sütun düzeyindeki ON EMPTY/ON ERROR davranışı JSON_VALUE ve JSON_QUERY ile aynıdır; tablonun sonuna yazılan ERROR ON ERROR ya da EMPTY ON ERROR ise yalnızca üst seviye yolun hatalarını etkiler. JSON_TABLE SELECT dışında UPDATE, DELETE ve MERGE içinde de veri kaynağı olarak kullanılabilir. Gruplama için GROUP BY ve aggregate, yanal birleşme için JOIN ve LATERAL yazılarına bakabilirsiniz.

IS JSON ile doğrulama

IS JSON yüklemi bir metnin JSON olarak ayrıştırılıp ayrıştırılamayacağını hata üretmeden sınar. OBJECT, ARRAY, SCALAR ile tür, WITH UNIQUE KEYS ile tekrarlanan anahtar kontrolü eklenebilir:

sql
SELECT v AS deger, v IS JSON AS json_mu, v IS JSON OBJECT AS nesne_mi, v IS JSON ARRAY AS dizi_mi,
       v IS JSON SCALAR AS skaler_mi, v IS JSON WITH UNIQUE KEYS AS tekil_anahtar
FROM (VALUES ('{"a": 1}'), ('[1, 2]'), ('"metin"'), ('{"a": 1, "a": 2}'), ('{bozuk}')) AS t(v);
text
      deger       | json_mu | nesne_mi | dizi_mi | skaler_mi | tekil_anahtar 
------------------+---------+----------+---------+-----------+---------------
 {"a": 1}         | t       | t        | f       | f         | t
 [1, 2]           | t       | f        | t       | f         | t
 "metin"          | t       | f        | f       | t         | t
 {"a": 1, "a": 2} | t       | t        | f       | f         | f
 {bozuk}          | f       | f        | f       | f         | f
(5 rows)

Gelen ham veriyi metin olarak saklayıp yine de yapısını zorlamak isterseniz bu yüklemi bir CHECK kısıtında kullanabilirsiniz:

sql
CREATE TABLE gelen_istek (id int, govde text CHECK (govde IS JSON OBJECT));
INSERT INTO gelen_istek VALUES (1, '{"olay": "giris"}');
INSERT INTO gelen_istek VALUES (2, '[1, 2]');
text
ERROR:  new row for relation "gelen_istek" violates check constraint "gelen_istek_govde_check"
DETAIL:  Failing row contains (2, [1, 2]).

Kısıtların genel kullanımı PRIMARY KEY, FOREIGN KEY ve kısıtlar yazısında.

Hangi aracı ne zaman kullanmalı?

Hangi aracı ne zaman kullanmalı? Tek bir alanı SQL tipinde almak, Nesne ya da dizi parçası almak, WHERE içinde koşullu varlık testi, Yüklem ile filtre
İhtiyaçUygun araç
Tek bir alanı SQL tipinde almakJSON_VALUE ya da ->> ve tip dönüşümü
Nesne ya da dizi parçası almakJSON_QUERY ya da ->
WHERE içinde koşullu varlık testi@? ya da JSON_EXISTS
Yüklem ile filtre@@ ya da jsonb_path_match
Eşleşen bütün değerleri almakjsonb_path_query, jsonb_path_query_array
JSON dizisini satırlara açmakJSON_TABLE ya da jsonb_array_elements
Metnin geçerli JSON olduğunu sınamakIS JSON

Sık yapılan hatalar

  • JSON_VALUE ve JSON_QUERY sonuçlarındaki beklenmeyen NULL'ları veri eksikliği sanmak: çoğu zaman sessizce yutulan bir hatadır. ERROR ON ERROR ile test edin.
  • JSON_VALUE ile dizi ya da nesne almaya çalışmak; bunun için JSON_QUERY.
  • Varsayılan jsonb dönüşüyle OMIT QUOTES kullanıp NULL almak; RETURNING text ekleyin.
  • Filtreyi yanlış adıma yazmak: $.kalemler[*].urun ? (@.adet > 2) ürün adı üzerinde adet arar ve hiçbir şey bulmaz; filtre kalem nesnesinden sonra gelmelidir.
  • @@ ile standart filtreli yol, @? ile yüklem ifadesi kullanmak.
  • Lax modda .** kullanıp tekrarlanan sonuçlar almak; bu erişimci için strict mod.
  • Kullanıcı girdisini yol metnine birleştirmek yerine PASSING ya da vars ile değişken geçirmemek.

Sık Sorulan Sorular

JSON_VALUE ile ->> arasında ne fark var?

Basit bir anahtar için ikisi de metin döndürür. JSON_VALUE SQL standardıdır, jsonpath filtrelerini ve değişkenleri destekler, RETURNING ile tip dönüşümünü ve ON EMPTY/ON ERROR ile eksik ve hatalı veriyi tek ifadede yönetir. ->> daha kısadır ve hata vermeden NULL döndürür.

JSON_TABLE ile jsonb_array_elements arasında nasıl seçim yaparım?

Bir diziyi yalnızca satırlara açıp bir iki alan alacaksanız jsonb_array_elements yeterlidir. Çok sayıda tipli sütun, iç içe seviyeler, sıra numarası ve eksik alan için yedek değer gerekiyorsa JSON_TABLE tek bir tanımla daha okunaklı bir sorgu verir.

jsonpath sorguları indeks kullanır mı?

@? ve @@ operatörleri jsonb üzerindeki GIN indeksini kullanabilir. Belgelere göre jsonb_ops operatör sınıfı .* ve .** erişimcilerini de destekler, jsonb_path_ops desteklemez. İndeks türleri için PostgreSQL indeks türleri.

PostgreSQL 18 düzeltme sürümlerinde SQL/JSON ile ilgili ne değişti?

PostgreSQL 18.6 sürüm notları, ON EMPTY/ON ERROR içindeki DEFAULT değerlerinin doğru tip niteleyicisine dönüştürülmesi, jsonpath .decimal() yönteminin hatalı hassasiyet ya da ölçekte sert hata vermemesi ve IS JSON ile ilgili bir çökme gibi düzeltmeler içerir. Bu yüzden kullandığınız 18.x sürümünün güncel olması önerilir.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: PostgreSQL öğrenme rehberi · PostgreSQL JSONB kullanımı · array (dizi) tipi · tarih ve saat · GROUP BY ve aggregate · JOIN ve LATERAL · CSV ve JSON dönüşümü