PostgreSQL Antipatternləri: "Yalnız bir olmalıdır!"

SQL-də siz "necə" yerinə yetirilməli olduğunu deyil, "nə" əldə etmək istədiyinizi təsvir edirsiniz. Buna görə də SQL sorğularının “eşitdiyi kimi yazılır” üslubunda işlənib hazırlanması problemi öz şərəfli yerini tutur. SQL-də şərtlərin hesablanması xüsusiyyətləri.

Bu gün, son dərəcə sadə nümunələrdən istifadə edərək, bunun istifadə kontekstində nəyə səbəb ola biləcəyinə baxaq GROUP/DISTINCT и LIMIT onlarla.

İndi sorğuda yazsanız "Əvvəlcə bu işarələri birləşdirin, sonra bütün dublikatları atın, yalnız biri qalmalıdır hər açar üçün kopyalayın" - əlaqə ümumiyyətlə lazım olmasa belə, tam olaraq belə işləyəcək.

Və bəzən şanslısınız və o, "sadəcə işləyir", bəzən performansa xoşagəlməz təsir göstərir, bəzən isə tərtibatçının nöqteyi-nəzərindən tamamilə gözlənilməz effektlər verir.

PostgreSQL Antipatternləri: "Yalnız bir olmalıdır!"
Yaxşı, bəlkə o qədər də möhtəşəm deyil, amma...

"Şirin cütlük": QOŞULUN + FƏRQLİ

SELECT DISTINCT
  X.*
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
WHERE
  Y.bool_condition;

Nə istədikləri bəlli olardı yerinə yetirilən şərtlə bağlı Y-də qeydlər olan X qeydlərini seçin. vasitəsilə sorğu yazdı JOIN - bir neçə dəfə bəzi pk dəyərləri əldə etdi (Y-də dəqiq neçə uyğun giriş var). Necə aradan qaldırmaq olar? Əlbəttə DISTINCT!

Hər bir X-rekord üçün bir neçə yüz əlaqəli Y-rekord olduğu və sonra dublikatların qəhrəmancasına silinməsi xüsusilə “sevindiricidir”...

PostgreSQL Antipatternləri: "Yalnız bir olmalıdır!"

Necə düzəltmək olar? Başlamaq üçün problemin dəyişdirilə biləcəyini anlayın “Y-də yerinə yetirilmiş şərtlə bağlı AZAL BİR olan X qeydlərini seçin” - Axı Y-rekordunun özündən heç nəyə ehtiyacımız yoxdur.

İç-içə VAR

SELECT
  *
FROM
  X
WHERE
  EXISTS(
    SELECT
      NULL
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  );

PostgreSQL-in bəzi versiyaları başa düşür ki, EXISTS-də ortaya çıxan ilk girişi tapmaq kifayətdir, köhnələri isə yox. Ona görə də həmişə qeyd etməyi üstün tuturam LIMIT 1 daxilində EXISTS.

YANAL QOŞULUN

SELECT
  X.*
FROM
  X
, LATERAL (
    SELECT
      Y.*
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  ) Y
WHERE
  Y IS DISTINCT FROM NULL;

Eyni seçim, zərurət yarandıqda, tapılmış əlaqəli Y qeydindən bəzi məlumatları dərhal qaytarmağa imkan verir. Bənzər bir seçim məqalədə müzakirə olunur "PostgreSQL Antipatternləri: nadir rekord JOIN-in ortasına çatacaq".

“Niyə daha çox ödəməlisiniz”: DISTINCT [ON] + LIMIT 1

Bu cür sorğu çevrilmələrinin əlavə faydası aşağıdakı hallarda olduğu kimi onlardan yalnız bir və ya bir neçəsinə ehtiyac olduqda qeydlərin axtarışını asanlıqla məhdudlaşdırmaq imkanıdır:

SELECT DISTINCT ON(X.pk)
  *
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;

İndi sorğunu oxuyuruq və DBMS-nin nə etməyi təklif etdiyini anlamağa çalışırıq:

  • işarələri birləşdirir
  • X.pk tərəfindən unikal
  • qalan qeydlərdən birini seçin

Bəs siz nə əldə etdiniz? "Sadəcə bir giriş" unikal olanlardan - və əgər bu qeyri-adi olanlardan birini götürsək, nəticə birtəhər dəyişəcəkmi?.. “Və heç bir fərq yoxdursa, niyə daha çox pul ödəyəsiniz?”

SELECT
  *
FROM
  (
    SELECT
      *
    FROM
      X
    -- сюда можно подсунуть подходящих условий
    LIMIT 1 -- +1 Limit
  ) X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;

Və eyni mövzu ilə GROUP BY + LIMIT 1.

“Mən sadəcə xahiş etməliyəm”: gizli QRUP + LİMİT

Oxşar şeylər fərqli şəkildə baş verir boşluq olmayan çeklər sorğu irəlilədikcə işarələr və ya CTE-lər:

...
CASE
  WHEN (
    SELECT
      count(*)
    FROM
      X
    LIMIT 1
  ) = 0 THEN ...

Ümumi funksiyalar (count/min/max/sum/...) hətta açıq göstərişlər olmadan bütün dəstdə uğurla icra edilir GROUP BY. Yalnız ilə LIMIT çox mehriban deyillər.

Tərtibatçı düşünə bilər "Əgər orada qeydlər varsa, o zaman mənə LIMIT-dən çox ehtiyac yoxdur". Amma bunu etmə! Çünki baza üçün bu:

  • istədiklərini saysınlar bütün qeydlərə görə
  • istədikləri qədər sətir verin

Hədəf şərtlərindən asılı olaraq, aşağıdakı əvəzetmələrdən birini etmək məqsədəuyğundur:

  • (count + LIMIT 1) = 0 haqqında NOT EXISTS(LIMIT 1)
  • (count + LIMIT 1) > 0 haqqında EXISTS(LIMIT 1)
  • count >= N haqqında (SELECT count(*) FROM (... LIMIT N))

"Qramlarda nə qədər asmaq olar": DISTINCT + LIMIT

SELECT DISTINCT
  pk
FROM
  X
LIMIT $1

Sadəlövh bir tərtibatçı sorğunun icrasını dayandıracağına səmimi şəkildə inana bilər. rastlaşdığımız ilk fərqli dəyərlərin 1 dollarını tapan kimi.

Gələcəkdə bu, yeni bir qovşaq sayəsində işləyə bilər və işləyəcək İndeks Skanını Atlayın, hazırda icrası üzərində iş gedir, lakin hələ yox.

Hələlik birinci bütün qeydlər alınacaq, unikaldır və yalnız onlardan tələb olunan məbləğ geri qaytarılacaq. Belə bir şey istəsək xüsusilə kədərlidir $ 1 = 4, və cədvəldə yüz minlərlə qeyd var...

Boş yerə kədərlənməmək üçün rekursiv sorğudan istifadə edək PostgreSQL Wiki-dən "DISTINCT kasıblar üçündür":

PostgreSQL Antipatternləri: "Yalnız bir olmalıdır!"

Mənbə: www.habr.com

Добавить комментарий