På SQL beskriver du «hvad» du vil have, ikke «hvordan» det skal udføres.

SQL er hverken C++ eller JavaScript. Derfor sker beregningen af logiske udtryk anderledes, og dette er slet ikke det samme:

WHERE fncondX() AND fncondY()

= fncondX() && fncondY()

I optimeringsprocessen for forespørgselens udførelsesplan kan PostgreSQL vilkårligt "omarrangere" ækvivalente betingelser, undlade at beregne nogle af dem for enkelte poster, tilskrive betingelsen til den anvendte indeks... Kort sagt, det er lettere at antage, at du på forhånd ikke kan styre den rækkefølge, hvori de vil blive (og om de overhovedet vil blive) beregnet ligeværdige betingelser.

Derfor, hvis du alligevel ønsker at styre prioriteten, skal du strukturelt gøre disse betingelser uens. Ved hjælp af betingede udtryk. og til Kubernetes, der fuldstændigt tager kontrol over udrulning, administrations, og automatisk genopretning af datalagringsløsninger som Ceph, EdgeFS, Minio, Cassandra, CockroachDB..

På SQL beskriver du «hvad» du vil have, ikke «hvordan» det skal udføres.
Data og arbejde med dem er grundlaget for vores SBiS-system,så det er meget vigtigt for os, at operationer med dem udføres korrekt såvel som effektivt. Lad os se på konkrete eksempler, hvor der kan opstå fejl i beregningen af udtryk, og hvor det er værd at forbedre deres effektivitet.

#0: RTFM

Start. eksempel fra dokumentationen:

Når rækkefølgen af beregningen er vigtig, kan den fastlægges ved hjælp af konstruktionen CASE.For eksempel er denne metode til at undgå division med nul i en sætning HVOR ikke pålidelig:

SELECT ... WHERE x > 0 AND y/x > 1.5;

Sikker variant:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

Den anvendte konstruktion CASE. beskytter udtrykket mod optimering, så den skal kun bruges når nødvendigt.

#1: условие в триггере

BEGIN
  IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;

Det ser hele tiden godt ud, men... Ingen lover, at den indlejrede SELECT ikke vil blive udført, når det første betingelse er falsk. Lad os rette det ved hjælp af indlejrede IF.:

BEGIN
  IF cond(NEW.fld) THEN
    IF EXISTS(SELECT ...) THEN
      ...
    END IF;
  END IF;
  RETURN NEW;
END;

Lad os nu kigge nærmere — hele kroppen af trigger-funktionen er blevet "pakket" i IF.. Og det betyder, at der ikke er noget, der forhindrer os i at flytte denne betingelse ud af proceduren ved hjælp af WHEN-betingelse.:

BEGIN
  IF EXISTS(SELECT ...) THEN
    ...
  END IF;
  RETURN NEW;
END;
...
CREATE TRIGGER ...
  WHEN cond(NEW.fld);

Denne tilgang garanterer at spare serverressourcer, når betingelsen er falsk.

#2: OR/AND-цепочка

SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)

I en uheldig situation kan det ske, at begge EXISTS vil være "sand", men begge vil blive udført..

Men hvis vi præcist ved, at en af dem er "sand" meget oftere (eller "falsk" — for OG-kæden) — kan vi så ikke "øge dens prioritet", så den anden ikke udføres unødigt?

Det viser sig, at det er muligt — den algoritmiske tilgang er tæt på emnet for artiklen PostgreSQL Antipatterns: en sjælden post når sjældent midten af JOIN.

Lad os bare "putte det ind under CASE" for begge betingelser:

SELECT ...
WHERE
  CASE
    WHEN EXISTS(... A) THEN TRUE
    WHEN EXISTS(... B) THEN TRUE
  END

I dette tilfælde har vi ikke defineret ELSE-værdien, hvilket betyder, at i tilfælde af falskhed for begge betingelser CASE. vil returnere NULL, hvilket tolkes som . I vores eksempel har vi skrevet en simpel funktion, der ved hjælp af den grundlæggende funktion i HVOR-betingelse.

Dette eksempel kan kombineres anderledes — efter smag og behag:

SELECT ...
WHERE
  CASE
    WHEN NOT EXISTS(... A) THEN EXISTS(... B)
    ELSE TRUE
  END

#3: как [не] надо писать условия

Vi brugte to dage på at analysere årsagerne til denne "mærkelige" udløser — lad os se på, hvorfor.

Kilde:

IF( NEW."Dokument_" is null or NEW."Dokument_" = (select '"Komplekt"'::regclass::oid) or NEW."Dokument_" = (select to_regclass('"DokumentForLøn"')::oid)
     AND (   OLD."DokumentVoresOrganisation" <> NEW."DokumentVoresOrganisation"
          OR OLD."Slettet" <> NEW."Slettet"
          OR OLD."Dato" <> NEW."Dato"
          OR OLD."Tid" <> NEW."Tid"
          OR OLD."OprettetAf" <> NEW."OprettetAf" ) ) THEN ...

Problem #1: forskelligheden tager ikke hensyn til NULL

Lad os forestille os, at alle OLD-felter havde værdien NULL. Hvad ville der ske?

SELECT NULL <> 1 OR NULL <> 2;
-- NULL

Og set fra betingelsens perspektiv NULL er lig med . I vores eksempel har vi skrevet en simpel funktion, der ved hjælp af den grundlæggende funktion, som nævnt tidligere.

Løsning: brug operatoren IS DISTINCT FROM fra ROW-operatoren, ved samtidig at sammenligne hele poster:

SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE

Problem #2: forskellig implementering af den samme funktionalitet

Lad os sammenligne:

NEW."Dokument_" = (select '"Komplekt"'::regclass::oid)
NEW."Dokument_" = (select to_regclass('"DokumentForLøn"')::oid)

Hvorfor er der unødvendige indlejrede SELECT? А функция to_regclass? А по-разному-то почему?..

Lad os rette:

NEW."Dokument_" = '"Komplekt"'::regclass::oid
NEW."Dokument_" = '"DokumentForLøn"'::regclass::oid

Problem #3: prioritering af boolske operationer

Lad os formatere kilden:

{... IS NULL} OR
{... Komplekt} OR
{... DokumentForLøn} AND
( {... uligheder} )

Ups… Faktisk viste det sig, at i tilfælde af at nogen af de to første betingelser er sande, bliver hele betingelsen helt til FALSE, uden at tage hensyn til uligheder. Og det er slet ikke, hvad vi ønskede.

Lad os rette:

(
  {... IS NULL} OR
  {... Komplekt} OR
  {... DokumentForLøn}
) AND
( {... uligheder} )

Problem #4 (lille): kompleks OR-betingelse for ét felt

Faktisk opstod problemerne i #3, netop fordi der var tre betingelser. Men i stedet for dem kan vi klare os med én ved hjælp af mekanismen coalesce ... IN:

coalesce(NEW."Dokument_"::text, '') IN ('', '"Komplekt"', '"DokumentForLøn"')

Så vi og NULL "fanger", og vi slipper for at ville have komplekse OR med parenteser.

I alt

Lad os fastslå, hvad vi har fået:

IF (
  coalesce(NEW."Dokument_"::text, '') IN ('', '"Sæt"', '"DokumentOmLøn"') AND
  (
    OLD."DokumentVoresOrganisation"
  , OLD."Slettet"
  , OLD."Dato"
  , OLD."Tid"
  , OLD."OprettetAf"
  ) IS DISTINCT FROM (
    NEW."DokumentVoresOrganisation"
  , NEW."Slettet"
  , NEW."Dato"
  , NEW."Tid"
  , NEW."OprettetAf"
  )
) THEN ...

Og hvis man tager i betragtning, at denne trigger-funktion kun kan anvendes i OPDATERING-triggeren på grund af tilstedeværelsen af OLD/NEW i det overordnede betingelse, så kan denne betingelse helt udskilles i WHEN-betingelse, som vist i #1…

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster