CodeGym /Kurslar /SQL SELF /JSONB ilə mürəkkəb sorğu nümunələri

JSONB ilə mürəkkəb sorğu nümunələri

SQL SELF
Səviyyə , Dərs
Mövcuddur

Əvvəlki dərslərdə biz JSONB-nin əsaslarını öyrəndik: necə yaratmaq, dəyişmək və məlumat çıxarmaq olar. İndi isə əsl sınaq vaxtıdır — bu data tipinin gücünü göstərən mürəkkəb sorğulara baxacağıq.

Təsəvvür elə bir internet-mağaza var və orda məhsullar kataloqu var. Hər məhsulun əsas məlumatı var (ad, ID), amma xüsusiyyətləri tam fərqli ola bilər: noutbukda RAM və CPU var, geyimdə — ölçülər və materiallar, kitabda — müəlliflər və janrlar. Bütün bunları ayrı-ayrı cədvəllərdə saxlamaq? Narahatdır. JSONB-də? Əladır! Bəs necə edək ki, məsələn, müəyyən bir brendin bütün məhsullarını tapaq, onları qiymətə görə sıralayaq və ya kateqoriyalar üzrə statistika çıxaraq? Necə işləyək o məlumatlarla ki, onlar adi sütunlarda yox, JSON strukturunun içində gizlənib?

Bu gün biz real ssenarilərə baxacağıq: sadə filtrdən tutmuş qruplaşdırma və aqreqasiyalı kompleks sorğulara qədər. Görəcəksən ki, JSONB PostgreSQL-i istənilən məlumat üçün çevik bir alətə çevirir.

JSONB-də məlumatların filtrasiyası

Filtrasiya — elə bil çay süzgəcidir: lazım olanı saxlayırsan, qalanı atırsan. JSONB ilə isə daha maraqlıdır, çünki biz təkcə adi sütunlara görə yox, həm də JSON strukturunun dərinliyində gizlənmiş məlumatlara görə də filtrasiya edə bilirik.

JSONB üçün filtrasiya operatorları:

  • @> — "JSONB-də var". Yoxlayır ki, JSONB obyektində göstərilən alt-məlumat var, ya yox.
  • ? — "Açar mövcuddur". Yoxlayır ki, göstərilən açar JSONB obyektində var, ya yox.
  • ?| — "Açarların hər hansı biri mövcuddur". Yoxlayır ki, göstərilən açarlardan ən azı biri var, ya yox.
  • ?& — "Bütün açarlar mövcuddur". Yoxlayır ki, göstərilən bütün açarlar var, ya yox.

Nümunə: açara və onun dəyərinə görə filtrasiya. Tutaq ki, bizdə products adlı cədvəl var və details sütununda məhsullar haqqında JSONB məlumatı saxlanılır:

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT,
    details JSONB
);

Məlumat nümunəsi:

INSERT INTO products (name, details) VALUES
('Laptop', '{"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}'),
('Smartphone', '{"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}'),
('Tablet', '{"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}');

Nəticə:

id name details
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}

Bütün brand "Apple" olan məhsulları tapmaq üçün:

SELECT *
FROM products 
WHERE details @> '{"brand": "Apple"}';

Nəticə:

id name details
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}

Əgər sən bütün məhsulları tapmaq istəyirsənsə ki, orda specs açarı var, ? operatorundan istifadə et:

SELECT *
FROM products 
WHERE details ? 'specs';

Nəticə:

id name details
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}

Bütün sətirlərdə details sahəsi və specs açarı var.

JSONB-də məlumatların sıralanması

Bəzən sənə lazım olur ki, məlumatları adi sütunlara görə yox, JSONB-nin içindəki dəyərlərə görə sıralayasan. Bunun üçün ->> (dəyəri mətn kimi çıxarır) və CAST operatorlarından istifadə edə bilərsən.

Nümunə: məhsulları qiymətə görə sıralayaq:

SELECT *
FROM products 
ORDER BY (details->>'price')::INTEGER;

Nəticə:

id name details
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}

JSONB-də məlumatların qruplaşdırılması

Qruplaşdırma sənə imkan verir ki, məlumatları aqreqasiya edəsən və statistika çıxarasan. Məsələn, hər brendə neçə məhsul düşür, onu öyrənə bilərsən.

Nümunə: hər brend üçün məhsul sayını hesablayaq:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
GROUP BY details->>'brand';

Nəticə:

brand product_count
Dell 1
Apple 1
Samsung 1

Praktik nümunələr

Filtrasiya və qruplaşdırma. Gəlin, hər brend üçün qiyməti 600-dən yuxarı olan məhsulların sayını hesablayaq:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
WHERE (details->>'price')::INTEGER > 600
GROUP BY details->>'brand';

Nəticə:

brand product_count
Dell 1
Apple 1

Qruplaşdırmadan sonra sıralama. İndi isə brendləri məhsul sayına görə azalan sırada sıralayaq:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
GROUP BY details->>'brand'
ORDER BY product_count DESC;

Kompleks sorğu: filtrasiya, sıralama, qruplaşdırma

Tutaq ki, sən istəyirsən ki, qiyməti 600-dən yuxarı olan məhsulları olan brendləri tapasan və hər brend üçün ən ucuz məhsulu seçəsən. Bax belə etmək olar:

WITH filtered_products AS (
    SELECT *
    FROM products
    WHERE (details->>'price')::INTEGER > 600
)
SELECT
    details->>'brand' AS brand,
    MIN((details->>'price')::INTEGER) AS min_price
FROM filtered_products
GROUP BY details->>'brand'
ORDER BY min_price;

Nəticə:

brand min_price
Apple 800
Dell 1200

Tipik səhvlər və məsləhətlər

Səhv: Operatorların səhv istifadəsi. ->->> operatorlarını qarışdırma: birincisi obyekt qaytarır, ikincisi isə mətn dəyəri.

Səhv: Performans problemləri. Əgər tez-tez mürəkkəb sorğular edirsənsə, JSONB sütununda GIN indeksi yarat.

Səhv: Tip problemləri. JSONB-dən çıxan dəyərlər string-dir, ona görə CAST istifadə etməyi unutma.

İndeks yaratmaq nümunəsi:

CREATE INDEX idx_products_details ON products USING GIN (details);

İndi details @> '{"brand": "Apple"}' kimi filtrasiya daha sürətli işləyəcək.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION