PostgreSQLi antipatternid: „JÀÀb ainult ĂŒks!“

SQLis kirjeldad sa, „mida“ soovid saada, mitte „kuidas“ see peaks toimuma. SeetĂ”ttu on SQL-pĂ€ringute koostamine stiilis „nagu kĂ”lab, nii kirjutatakse” oma kohal. erinevuste arvutamine SQLis.

TĂ€na vaatame ĂŒli lihtsate nĂ€idete kaudu, kuhu see vĂ”ib viia seoses GROUP/DISTINCT ja LIMIT koos nendega.

Kui sa oled pĂ€ringus kirjutanud „kĂ”igepealt ĂŒhenda need tabelid ja seejĂ€rel eemalda kĂ”ik dubleed, peab alles jÀÀma ainult ĂŒks eksemplar iga vĂ”tme kohta“ — just nii see töötabki, isegi kui liitmine polnud ĂŒldse vajalik.

Ja mĂ”nikord on Ă”nne ja see „lihtsalt töötab“, mĂ”nikord — mĂ”jutab see negatiivselt jĂ”udlust, ja mĂ”nikord annab see arendaja vaatenurgast tĂ€iesti ootamatuid efekte.

PostgreSQLi antipatternid: „JÀÀb ainult ĂŒks!“
No, vÔib-olla mitte nii dramaatiliselt, aga


„Magus paar”: JOIN + DISTINCT

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

Kuidas oleks mĂ”istlik, et sooviti valida sellised kirjed X, mille puhul Y-st on seotud tĂ€idetava tingimusega. Kirjutasid pĂ€ringu lĂ€bi JOIN — saadi mĂ”ned pk vÀÀrtused mitu korda (tĂ€pselt nii palju kui sobivaid kirjeid Y osutus). Kuidas eemaldada? Loomulikult. DISTINCT!

Eriti "rÔÔmustab", kui iga X-kirje kohta leidub mitusada seotud Y-kirjet, ja siis kangelaslikult eemaldatakse duplikaadid


PostgreSQLi antipatternid: „JÀÀb ainult ĂŒks!“

Kuidas parandada? Alustuseks tunnistada, et ĂŒlesannet saab muuta jĂ€rgmistele. „valida sellised X-kirjed, mille Y-s on KUIDAGI ÜKS seotud tingimusega“ — sest Y-kirjast pole meile midagi vajalik.

Sisemine EXISTS

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

MĂ”ned PostgreSQL versioonid mĂ”istavad, et EXISTS-is piisab esimese leidmise kirjega, vanemad — mitte. SeetĂ”ttu eelistan alati nĂ€idata LIMIT 1 sisemine EXISTS.

LATERAL JOIN

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;

Sama variant vĂ”imaldab vajadusel kohe tagasi tuua ka mingisuguseid andmeid leitud seosest Y-kirjest. Sarnast varianti kĂ€sitleti artiklis „PostgreSQL Antipatterns: harva saavutab kirje JOIN-i keskpaika“.

„Miks maksta rohkem“: DISTINCT [ON] + LIMIT 1

Seda tĂŒĂŒpi pĂ€ringu ĂŒlemineku tĂ€iendav eelis on vĂ”imalus hĂ”lpsasti piirata salvestuste lĂ€bivaatamist, kui on vaja ainult ĂŒhte vĂ”i mitut neist, nagu jĂ€rgmisel juhul:

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

NĂŒĂŒd loeme pĂ€ringu ja pĂŒĂŒame aru saada, mida andmebaasi haldussĂŒsteem pakub:

  • seome tabelid
  • teeme X.pk alusel unikaalseks
  • valime jÀÀkidest mingi ĂŒhe

Nii et mida me saime? „Mingi ĂŒks salvestus” unikaalsetest — aga kui vĂ”tta see ĂŒks mitteunikaalsetest, kas tulemus muutub kuidagi?
 „Aga kui vahet pole, miks maksma rohkem?”

SELECT
  *
FROM
  (
    SELECT
      *
    FROM
      X
    -- siia saab sobivaid tingimusi lisada
    LIMIT 1 -- +1 Limiteerimine
  ) X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;

Ja tÀpselt sama teema GROUP BY + LIMIT 1.

„Mul on ainult kĂŒsida”: varjatud GROUP + LIMIT

Sarnaseid asju kohtab erinevate tĂŒhjuse kontrollide tabeli vĂ”i CTE lĂ€bimisel pĂ€ringu kĂ€igus:

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

Agregeerimisfunktsioonid (count/min/max/sum/...) töötavad edukalt kogu kogumi peal, isegi ilma selge viiteta GROUP BY. Ainult sellega on LIMIT nad pÀris hÀsti sÔbrad.

Arendaja vÔib arvata «kui seal on kirjed, siis mul ei tohi olla rohkem kui LIMIT». Aga nii ei tohi teha! Sest andmebaasi jaoks on see:

  • arvuta, mida nad soovivad kĂ”igi rekordite kohta
  • anna tagasi nii palju ridu, kui palutakse

SĂ”ltuvalt sihttingimustest on siin kohane teha ĂŒks asendusi:

  • (count + LIMIT 1) = 0 jĂ€rgnevaga NOT EXISTS(LIMIT 1)
  • (count + LIMIT 1) > 0 jĂ€rgnevaga EXISTS(LIMIT 1)
  • count >= N jĂ€rgnevaga (SELECT count(*) FROM (... LIMIT N))

«Mitu grammi kaaluda»: DISTINCT + LIMIT

SELECT DISTINCT
  pk
FROM
  X
LIMIT $1

Naivne arendaja vÔib siiralt arvata, et pÀringu tÀitmine peatub, niipea kui leiame $1 esimestest erinevatest vÀÀrtustest.

Kord tulevikus vÔib see tÔesti toimida uue sÔlme tÔttu Index Skip Scan, mille rakendamine on praegu töös, aga praegu - ei ole.

Esialgu kĂ”ik kirjed on vĂ€lja tĂ”mmatud, unikaalsed, ja ainult neist tagastatakse, kui palju palutakse. Eriti kurb on, kui tahtsime midagi sarnast $1 = 4, aga tabelis on — sadu tuhandeid


Et mitte asjatult kurvastada, kasutame rekursiivset pÀringut «DISTINCT vaesetele» PostgreSQL Wiki-st:

PostgreSQLi antipatternid: „JÀÀb ainult ĂŒks!“

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster