|
<< İçindekileri Görüntülemek İçin Tıklayın >> Yer: Teknik Kılavuzlar > IVEDI SQL Kullanıcı Kılavuzu > 3. Veri tipleri > 3.6 JSON tipi ve SQL/JSON işlevleri |
JSON metin değil, kendi başına bir tiptir. Bir JSON yerine yazılan değer
yazılırken ayrıştırılır; geçersizse saklanmaz ve Invalid JSON text hatası verir
(SQLSTATE 22032). Saklanan biçim kanoniktir ve üç kuralı vardır:
Sayılar yeniden biçimlenmez: 1.50 öyle kalır. Bu üç kural PostgreSQL'in
jsonb tipiyle aynıdır; PostgreSQL arka ucunda sütun JSONB olur,
öteki motorlarda metin olarak saklanır ve doğrulamayı IVEDI yapar.
create table belge(id integer not null primary key, doc json);
insert into belge values (1, '{ "b" : 2 , "a" : 1 , "a" : 9 }');
select cast(doc as varchar(100)) from belge; --> {"a":9,"b":2}Belgeyi okumak için üç işlev vardır. Üçü de belgeyi JSON ya da düz metin olarak
alabilir:
| Yazım | Notlar |
|---|---|
JSON_VALUE(<belge>, <yol>) | Yolun ulaştığı skaler
değeri metin olarak verir. Yol bir nesneye ya da diziye ulaşırsa sonuç NULL'dur; onun için
JSON_QUERY vardır. JSON null değeri SQL NULL olur. JSON mantıksal değeri
metin sonuçta JSON yazımıyla döner ('true' / 'false');
RETURNING BOOLEAN verilirse mantıksal kalır. |
JSON_QUERY(<belge>, <yol>) | Yolun ulaştığı düğümü kanonik JSON metni olarak verir; nesne, dizi ya da skaler olabilir. |
JSON_EXISTS(<belge>, <yol>) | Yol bir düğüme ulaşıyor mu:
TRUE / FALSE. Belge ya da yol NULL ise sonuç NULL'dur. |
Yol dili şunlardan oluşur: $ (belgenin kökü), .ad ve
["ad"] (üye erişimi), .* (bütün üyeler), [n] (dizi indeksi,
sıfırdan başlar), [n to m] (aralık), [*] (bütün öğeler),
[last] ve [last - n] (sondan sayma), köşeli parantez içinde virgüllü
liste ([0, 2 to 4]), ?( … ) süzgeci ve başa yazılan
lax/strict kipi. Tanınmayan bir yazım sessizce boş dönmez,
Invalid SQL/JSON path hatası verir. Bu bir yazım hatasıdır, veri hatası
değil: SQLSTATE 42601 (syntax error); standardın 22…
sınıfı SQL/JSON veri durumlarına ayrılmıştır. Öğe metotları
(.type(), .size(), .keyvalue()) bilerek yoktur: karşılıkları
bu motorda SQL tarafındadır.
?( <yüklem> ) geldiği noktadaki düğümleri eler. Yüklemde @ o anki
düğüm, $ belgenin kökü demektir:
| Yazım | Notlar |
|---|---|
== != <> <
<= > >= | Karşılaştırma varoluşsaldır: iki taraf da birer dizi düğüm olabilir ve bir çift bile doğruysa yüklem doğrudur. Tipler uyuşmuyorsa sonuç bilinmeyendir ve süzgeç geçmez; sessiz bir tip zorlaması yapılmaz. |
&& || ! | Mantıksal bağlaçlar; parantezle gruplanabilir. |
exists( <yol> ) | Yol bir düğüme ulaşıyor mu. Yapı hatasını
yutar: strict kipinde bile hata değil false verir. |
starts with | Dizge öneki. |
like_regex "<desen>" [flag "<bayraklar>"] | Düzenli
ifade (XQuery söz dizimi, SUBSTRING_REGEX ailesiyle aynı). |
select * from json_table(:doc, '$.k[*]?(@.aktif == true && @.yas > 26)'
columns (ad varchar(20) path '$.ad')) jt;
-- süzgeç içinde $ belgenin köküdür
'$.k[*]?(@.yas > $.esik)'
'$.k[*]?(@.ad starts with "A")'
'$.k[*]?(exists(@.telefon))'Yolun başına lax ya da strict yazılabilir; öndeğer
lax'tır. İki kipin tek farkı, yapı uymadığında ne olduğudur:
| Durum | lax (öndeğer) | strict |
|---|---|---|
Dizi üzerinde .ad | Dizi bir düzey açılır, ad her
öğede aranır | Hata |
Dizi olmayanda [0] / [*] | Değer tek öğeli dizi sayılır | Hata |
| Sınır dışı indeks, bulunamayan üye | Sessizce boş | Hata |
select json_query(:doc, 'lax $.k.ad' with array wrapper); --> ["Ali","Veli"]
select json_query(:doc, 'strict $.k[*].ad' with array wrapper); --> ["Ali","Veli"]
select json_value(:doc, 'strict $.k.ad'); --> NULL (NULL ON ERROR)strict kipinin yapı hatası bir veri hatasıdır: ON
ERROR onu yakalar (öndeğer NULL ON ERROR olduğu için sessizce NULL olur) ve
ERROR ON ERROR yazılırsa hata olarak çıkar. Standardın üç ayrı durumu üç ayrı
SQLSTATE taşır: dizi beklenirken dizi olmayan SQL/JSON array not found
(22039), bulunamayan ya da nesne olmayanda aranan üye SQL/JSON member not
found (2203A), sınır dışı altsimge invalid SQL/JSON subscript
(22033). Bir yol yazım hatası ise bunun dışındadır ve hiçbir zaman
yutulmaz.
Joker, aralık ya da süzgeç içeren bir yol birden çok düğüme ulaşabilir.
JSON_EXISTS bunu olduğu gibi okur (bir düğüm bile varsa TRUE), ama
JSON_VALUE ile JSON_QUERY tek bir değer üretmek zorundadır: birden çok
eşleşme standardın kuralı gereği hata verir. Birden çok düğümü satırlara açmak
JSON_TABLE'ın işidir.
select json_value(doc, '$.adres.il') from belge; --> Ankara
select json_value(doc, '$.etiketler[1]') from belge; --> b
select json_query(doc, '$.adres') from belge; --> {"il":"Ankara","posta":"06000"}
select json_exists(doc, '$.adres.posta') from belge; --> TRUEÜç işlev de standardın yan tümcelerini kabul eder:
JSON_EXISTS(<belge>, <yol> [ TRUE | FALSE | UNKNOWN | ERROR ON ERROR ])
JSON_VALUE (<belge>, <yol> [ RETURNING <tip> ]
[ (NULL | ERROR | DEFAULT <ifade>) ON EMPTY ]
[ (NULL | ERROR | DEFAULT <ifade>) ON ERROR ])
JSON_QUERY (<belge>, <yol> [ RETURNING <tip> ]
[ WITHOUT [ARRAY] WRAPPER
| WITH [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER ]
[ (NULL | ERROR | EMPTY ARRAY | EMPTY OBJECT) ON EMPTY ]
[ (NULL | ERROR | EMPTY ARRAY | EMPTY OBJECT) ON ERROR ])Öndeğerler standardınkidir: NULL ON EMPTY ve
NULL ON ERROR; JSON_EXISTS'te FALSE ON ERROR. Yani
bozuk bir belge metni bu işlevlerde hata değil NULL verir; hata isteniyorsa
ERROR ON ERROR yazılır. (Bozuk metni bir JSON sütununa yazmak ayrı
şeydir ve orada hata verir.)
select json_value('{bozuk}', '$.a'); --> NULL
select json_value('{bozuk}', '$.a' error on error); --> hata
select json_value('{"a":42}', '$.a' returning integer); --> 42
select json_value('{"a":"abc"}', '$.a' returning integer
default -1 on error); --> -1
select json_value('{"a":1}', '$.yok' default 'yok' on empty); --> yok
select json_query('{"a":[1,2,3]}', '$.a[*]' with array wrapper); --> [1,2,3]
select json_query('{"a":1}', '$.yok' empty array on empty); --> []
select json_exists('{bozuk}', '$.a' unknown on error); --> NULLRETURNING sonucu istenen tipe çevirir ve bu dönüşümün hatası da
ON ERROR'un kapsamındadır; dışarıdan bir CAST ile sarmak aynı
şey değildir, o hata fırlatır. WITH CONDITIONAL ARRAY WRAPPER sonucu yalnızca zaten
tek bir dizi ya da nesne değilse diziye sarar; sarmalayıcı istenmediğinde yolun birden çok
düğüme ulaşması bir veri hatasıdır ve ON ERROR'a düşer.
Bir yol yazım hatası bu kapsamda değildir: ON ERROR onu yutmaz,
her zaman dışarı çıkar. Yutulan şey veri hatalarıdır: bozuk belge, boş sonuç, birden
çok eşleşme, dönüşüm hatası.
Bir metnin geçerli JSON olup olmadığı hata almadan sorulabilir; yüklemin varlık sebebi budur:
<ifade> IS [NOT] JSON [ VALUE | SCALAR | ARRAY | OBJECT ] [ WITH | WITHOUT UNIQUE KEYS ]Geçersiz metin hata değil FALSE verir. Girdi NULL ise sonuç
bilinmeyendir (NULL); bu IS NOT JSON için de böyledir. Ekler
yazılmazsa öndeğer VALUE (yani "geçerli bir belge mi") ve
WITHOUT UNIQUE KEYS'tir.
select '{"a":1}' is json; --> TRUE
select '{bozuk}' is json; --> FALSE
select '42' is json scalar; --> TRUE
select '[1,2]' is json object; --> FALSE
select '{"a":1,"a":2}' is json with unique keys; --> FALSEWITH UNIQUE KEYS yalnızca metin üzerinde ayırt edicidir.
JSON tipli bir değerde kanonik biçim yinelenen anahtarın sonuncusunu tutup
ötekileri attığı için yanıt her zaman TRUE'dur; aynı belge bir
varchar sütununda tutulursa FALSE döner.
Dört işlev belge üretir. Çağrı konum sıralıdır: standardın
KEY … VALUE … yazımı yerine MySQL, SQLite ve PostgreSQL'in
json_build_object/json_build_array işlevleriyle aynı şekil:
| Yazım | Notlar |
|---|---|
JSON_ARRAY(<değer>, …) | Argümanlardan bir dizi.
NULL argüman diziye girmez (standardın ABSENT ON NULL
öndeğeri). |
JSON_OBJECT(<anahtar>, <değer>, …) | Anahtar/değer
çiftlerinden bir nesne. NULL değer null olarak yazılır
(NULL ON NULL); anahtar NULL olamaz ve tek kalan argüman hata verir. |
JSON_ARRAYAGG(<değer>) | Grubun değerlerinden bir dizi
(ABSENT ON NULL). Hiç satır yoksa sonuç []'dir, NULL değil. |
JSON_OBJECTAGG(<anahtar>, <değer>) | Grubun
anahtar/değerlerinden bir nesne (NULL ON NULL). Hiç satır yoksa sonuç
{}'dir. |
Sonuç JSON tipindedir ve kanoniktir; yukarıdaki üç kural burada da geçerlidir:
aynı anahtar iki kez yazılırsa sonuncusu kalır. Bildirilen tipi JSON olan bir argüman
tırnaklanmaz, belge olarak gömülür; iç içe belge böyle kurulur. Ayırt eden şey
argümanın tipidir, içeriği değil: JSON gibi görünen bir metin yine de dizgedir.
select json_array(1, null, 'x') from dual; --> [1,"x"]
select json_object('b', 2, 'a', 1) from dual; --> {"a":1,"b":2}
select json_array(json_object('k', 1), '[1,2]') from dual; --> [{"k":1},"[1,2]"]
select g, json_objectagg(k, v) from t group by g; --> {"a":10,"b":null}JSON_TABLE bir belgeyi ilişkisel satırlara açar ve FROM'da bir kaynak
gibi durur:
JSON_TABLE(<belge>, '<kök yol>' [AS <ad>]
COLUMNS ( <kolon> [, <kolon>]... )
[PLAN (<plan>) | PLAN DEFAULT (<seçenekler>)])Kök yol bir dizi düğüm verir, tipik olarak '$.satirlar[*]', ve
her düğüm bir satırdır. Kolon dört şekilde yazılır:
| Kolon | Ne yapar |
|---|---|
<ad> <tip> [PATH '<yol>'] | Yolun ulaştığı
skaler değer (JSON_VALUE anlambilimi). PATH
yazılmazsa öndeğer yol kolonun yazıldığı adıdır; JSON anahtarları
büyük/küçük harfe duyarlıdır. |
<ad> <tip> FORMAT JSON [PATH …] | Düğümün
kendisi, kanonik JSON metni olarak (JSON_QUERY anlambilimi);
nesne ya da dizi böyle alınır. |
<ad> FOR ORDINALITY | Düğümün kendi dizisindeki 1 tabanlı sırası. |
NESTED [PATH] '<yol>' [AS <ad>] COLUMNS (…) | İç bir
diziyi aynı satırın altına açar; içinde yine her üç kolon türü ve yeni
NESTED'lar olabilir. |
Skaler kolonlara NULL | ERROR | DEFAULT <ifade> ardından ON EMPTY
ya da ON ERROR yazılabilir. Öndeğer ikisinde de NULL'dur;
ERROR seçilirse yol boş döndüğünde No SQL/JSON item hatası gelir (SQLSTATE
22035).
select * from json_table('{"k":[{"ad":"Ali","tel":["1","2"]},{"ad":"Veli","tel":[]}]}',
'$.k[*]' columns (
ad varchar(20) path '$.ad',
nested path '$.tel[*]' columns (n for ordinality, tel varchar(10) path '$')
)) jt;
--> Ali 1 1
--> Ali 2 2
--> Veli NULL NULLPLAN, bir düzeyin çocuklarıyla nasıl birleştiğini söyler: OUTER çocuk
satırı olmasa da üst satırı tutar, INNER tutmaz; kardeş NESTED'lar
arasında UNION satırları art arda ekler, CROSS kartezyen çarpar. Öndeğer
PLAN DEFAULT (OUTER, UNION)'dır; yukarıdaki örnekte Veli satırının
kalmasının sebebi budur. PLAN (kök OUTER (a CROSS b)) yazımı tek tek düzeyleri
adlandırır ve bunun için AS ile ad verilmiş olması gerekir.
JSON_TABLE FROM listesinde örtülü LATERAL'dır:
belgesi kendinden önce gelen bir kaynağın kolonu olabilir ve ayrıca LATERAL yazmak
gerekmez.
select d.id, jt.k
from dokuman d, json_table(d.doc, '$.e[*]' columns (k varchar(10) path '$.k')) jt;