PostgreSQL / Window функцүүд

Window функцүүд

Window функц бол PostgreSQL-н хамгийн хүчирхэг боломжуудын нэг юм. GROUP BY-аас ялгаатай нь window функц мөрүүдийг нэгтгэн нэг үр дүн буцаадаггүй — мөр тус бүрийг хэвээр нь үлдээж, зэрэгцээ мөрүүдийн утгыг харгалзан тооцоо хийдэг. Эхэндээ ойлгоход хэцүү санагдаж болно, гэхдээ жишээнүүдийг дагаж явбал тодорхой болно.

OVER() — window функцийн үндэс

OVER() түлхүүр үг нь нэгтгэх функцийг window функц болгон хувиргадаг. Хаалт дотор юу ч бичихгүй бол бүх мөрүүдийн дээгүүр нэг "цонх" тавина:

sql
-- products хүснэгтийн мөр бүрд дундаж үнийг харуулах
SELECT
    name,
    price,
    ROUND(AVG(price) OVER (), 0) AS нийт_дундаж,
    price - ROUND(AVG(price) OVER (), 0) AS зөрүү
FROM products;

GROUP BY ашиглавал бүлэглэгдэж нэг мөр болох байсан — гэвч OVER() бол эх мөрүүдийг хэвээр нь хадгалж бүх баганыг харуулна.

PARTITION BY — бүлгийн дотор тооцоо

PARTITION BY нь GROUP BY-тай ижил бүлэглэлт хийдэг ч мөрүүдийг устгахгүй:

sql
-- Категори тус бүрт дундаж үнийг мөр бүрд харуулах
SELECT
    name,
    category,
    price,
    ROUND(AVG(price) OVER (PARTITION BY category), 0) AS категорийн_дундаж
FROM products
ORDER BY category, price;

Үр дүн: 'электроник' бараануудад нэг дундаж, 'хувцас' бараануудад өөр дундаж харагдана — гэхдээ бүх мөр хэвээр байна.

ORDER BY OVER() — дараалсан тооцоо

OVER() дотор ORDER BY нэмбэл тухайн мөр хүртэлх дүнгийн нийлбэр (running total) гэх мэт тооцоо хийж болно:

sql
-- Бараануудыг үнээр эрэмбэлж, дүнгийн нийлбэрийг харуулах
SELECT
    name,
    price,
    SUM(price) OVER (ORDER BY price) AS хуримтлагдсан_дүн
FROM products
ORDER BY price;

ROW_NUMBER, RANK, DENSE_RANK

Эдгээр нь мөр тус бүрт дугаар буюу эрэмбэ өгдөг тусгай window функцүүд юм:

sql
SELECT
    name,
    category,
    price,
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS дугаар,
    RANK()       OVER (PARTITION BY category ORDER BY price DESC) AS зэрэглэл,
    DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) AS нягт_зэрэглэл
FROM products;

Гурвын ялгаа:

  • ROW_NUMBER() — ижил утга байсан ч давтагдахгүй дараалсан дугаар өгнө
  • RANK() — ижил утга тэнцүү зэрэглэл авах ба дараагийн зэрэглэл алгасна (1, 1, 3...)
  • DENSE_RANK() — ижил утга тэнцүү зэрэглэл авах ч алгасахгүй (1, 1, 2...)

Категори тус бүрээс хамгийн үнэтэй барааг олоход маш хялбар:

sql
-- Категори тус бүрийн хамгийн үнэтэй барааг олох
WITH ranked AS (
    SELECT
        name,
        category,
        price,
        ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS n
    FROM products
)
SELECT name, category, price
FROM ranked
WHERE n = 1;

LAG ба LEAD — өмнөх, дараагийн мөрийн утга

LAG() нь өмнөх мөрийн, LEAD() нь дараагийн мөрийн утгыг авна:

sql
SELECT
    name,
    price,
    LAG(price)  OVER (ORDER BY price) AS өмнөх_үнэ,
    LEAD(price) OVER (ORDER BY price) AS дараагийн_үнэ,
    price - LAG(price) OVER (ORDER BY price) AS өөрчлөлт
FROM products
ORDER BY price;

Энэ нь цаг цувааны өгөгдөл шинжлэхэд — жишээлбэл өдөр тус бүрийн борлуулалтын өсөлт, бууралтыг тооцоход — маш хэрэгтэй.

Дараагийн хичээлд:

Асуулгыг хурдасгадаг индекс-ийн үндсийг судалж, яагаад их өгөгдөл дээр индексгүй асуулга удаан болдгийг ойлгоно.