PostgreSQL Antipatterns: "Yalnızca bir tane olmalı!"

SQL'de, "nasıl" yürütülmesi gerektiğini değil, "neyi" başarmak istediğinizi tanımlarsınız. Dolayısıyla “Duyulduğu gibi yazılır” tarzında SQL sorguları geliştirme sorunu da ön plana çıkıyor. SQL'de koşulları hesaplamanın özellikleri.

Bugün son derece basit örnekler kullanarak bunun kullanım bağlamında nelere yol açabileceğini görelim. GROUP/DISTINCT и LIMIT onlarla.

Şimdi, eğer istekte yazdıysanız “önce bu işaretleri birleştirin ve ardından tüm kopyaları atın, sadece bir tane kalmalı her anahtar için kopyala" - bağlantıya hiç ihtiyaç duyulmasa bile tam olarak bu şekilde çalışacaktır.

Ve bazen şanslısınız ve "sadece işe yarıyor", bazen performans üzerinde hoş olmayan bir etkiye sahip oluyor ve bazen geliştiricinin bakış açısından tamamen beklenmedik etkiler veriyor.

PostgreSQL Antipatterns: "Yalnızca bir tane olmalı!"
Belki o kadar muhteşem olmayabilir ama...

“Tatlı çift”: KATIL + AYRIŞTIR

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

Ne istedikleri belli olacak Y'de yerine getirilen koşulla ilgili kayıtların bulunduğu X kayıtlarını seçin. aracılığıyla bir istek yazdı JOIN - birkaç kez bazı pk değerleri aldım (Y'de tam olarak kaç tane uygun girişin göründüğü). Nasıl kaldırılır? Kesinlikle DISTINCT!

Her X kaydı için birkaç yüz ilgili Y kaydının olması ve ardından kopyaların kahramanca kaldırılması özellikle "memnun edicidir"...

PostgreSQL Antipatterns: "Yalnızca bir tane olmalı!"

Nasıl düzeltilir? Başlangıç ​​olarak sorunun şu şekilde değiştirilebileceğini anlayın: "Y'de yerine getirilen koşulla EN AZ BİR BİRİNİN bulunduğu X kayıtlarını seçin" - sonuçta Y kaydının kendisinden hiçbir şeye ihtiyacımız yok.

İç içe MEVCUTLAR

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

PostgreSQL'in bazı sürümleri, EXISTS'te ortaya çıkan ilk girişi bulmanın yeterli olduğunu, eskilerin ise bunu yapmadığını anlıyor. Bu nedenle her zaman belirtmeyi tercih ederim LIMIT 1 içinde EXISTS.

YANAL BİRLEŞİM

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;

Aynı seçenek, gerekirse bulunan ilişkili Y kaydından bazı verilerin anında döndürülmesine olanak tanır. Makalede benzer bir seçenek tartışılıyor "PostgreSQL Antipatterns: nadir bir kayıt bir JOIN'in ortasına ulaşacak".

"Neden daha fazla ödeyesiniz": DISTINCT [ON] + LIMIT 1

Bu tür sorgu dönüşümlerinin ek bir yararı, aşağıdaki durumda olduğu gibi yalnızca bir veya birkaç tanesine ihtiyaç duyulması durumunda kayıt aramasını kolayca sınırlandırma yeteneğidir:

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

Şimdi isteği okuyoruz ve DBMS'nin ne yapması önerildiğini anlamaya çalışıyoruz:

  • işaretlerin bağlanması
  • X.pk tarafından benzersiz
  • kalan girişlerden birini seçin

Peki ne aldın? "Sadece bir giriş" benzersiz olanlardan - peki bunu benzersiz olmayanlardan alırsak sonuç bir şekilde değişir mi?.. “Peki fark yoksa neden daha fazla ödeyelim?”

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

Ve tamamen aynı konu GROUP BY + LIMIT 1.

"Sadece sormam gerekiyor": örtülü GROUP + LIMIT

Farklı zamanlarda benzer şeyler oluyor boşluk kontrolleri istek ilerledikçe işaretler veya CTE'ler:

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

Toplama işlevleri (count/min/max/sum/...) açık talimatlar olmadan bile setin tamamında başarıyla yürütülür GROUP BY. Sadece ile LIMIT pek dost canlısı değiller.

Geliştirici düşünebilir “Eğer orada kayıtlar varsa, o zaman LIMIT'den fazlasına ihtiyacım yok”. Ama bunu yapma! Çünkü taban için:

  • istediklerini say tüm kayıtlara göre
  • istedikleri kadar satır ver

Hedef koşullara bağlı olarak aşağıdaki değişikliklerden birinin yapılması uygundur:

  • (count + LIMIT 1) = 0 üzerinde NOT EXISTS(LIMIT 1)
  • (count + LIMIT 1) > 0 üzerinde EXISTS(LIMIT 1)
  • count >= N üzerinde (SELECT count(*) FROM (... LIMIT N))

“Gram cinsinden ağırlığı ne kadardır”: DISTINCT + LIMIT

SELECT DISTINCT
  pk
FROM
  X
LIMIT $1

Deneyimsiz bir geliştirici, isteğin yürütülmesinin durdurulacağına içtenlikle inanabilir. Karşımıza çıkan ilk farklı değerlerden 1$’ı bulduğumuz anda.

Gelecekte bir ara bu, yeni bir düğüm sayesinde işe yarayabilir ve çalışacaktır. Dizin Taramayı Atla, uygulaması şu anda üzerinde çalışılıyor, ancak henüz değil.

Şimdilik ilk tüm kayıtlar geri alınacak, benzersizdir ve talep edilen tutar yalnızca onlardan iade edilecektir. Eğer böyle bir şey istiyorsak özellikle üzücü 1 $ = 4ve tabloda yüzbinlerce kayıt var...

Boş yere üzülmemek için özyinelemeli bir sorgu kullanalım PostgreSQL Wiki'den "DISTINCT yoksullar içindir":

PostgreSQL Antipatterns: "Yalnızca bir tane olmalı!"

Kaynak: habr.com

Yorum ekle