SQL bukan C++, begitu juga JavaScript. Oleh itu, penilaian ungkapan logik adalah berbeza, dan ini bukan perkara yang sama sama sekali:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Semasa mengoptimumkan pelan pelaksanaan pertanyaan PostgreSQL
Oleh itu, jika anda masih mahu mengurus keutamaan, anda perlu secara berstruktur menjadikan syarat ini tidak sama rata dengan bersyarat
Data dan bekerja dengan mereka adalah asas
#0: RTFM
Bermula
Apabila susunan penilaian adalah penting, ia boleh diperbaiki dengan konstruk
CASE
. Sebagai contoh, cara ini untuk mengelakkan pembahagian dengan sifar dalam ayatWHERE
tidak boleh dipercayai:SELECT ... WHERE x > 0 AND y/x > 1.5;
Pilihan selamat:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Binaan yang digunakan
CASE
melindungi ungkapan daripada pengoptimuman, jadi ia hanya boleh digunakan apabila perlu.
#1: keadaan pencetus
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Segala-galanya nampaknya kelihatan baik, tetapi... Tiada siapa yang menjanjikan bahawa yang dilaburkan SELECT
tidak akan dilaksanakan jika syarat pertama adalah palsu. Betulkan dengan bersarang IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Sekarang mari kita lihat dengan teliti - seluruh badan fungsi pencetus ternyata "dibungkus" masuk IF
. Dan ini bermakna tiada apa yang menghalang kita daripada mengeluarkan keadaan ini daripada prosedur menggunakan WHEN
-syarat
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Pendekatan ini membolehkan anda menyimpan sumber pelayan dengan jaminan jika syarat itu palsu.
#2: rantai ATAU/DAN
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Jika tidak, ia boleh didapati bahawa kedua-duanya EXISTS
akan menjadi benar, tetapi kedua-duanya akan dilaksanakan.
Tetapi jika kita tahu dengan pasti bahawa salah satu daripadanya adalah "benar" lebih kerap (atau "palsu" - untuk AND
-chains) - adakah mungkin untuk "meningkatkan keutamaannya" supaya yang kedua tidak dilaksanakan sekali lagi?
Ternyata ia mungkin - pendekatan algoritma hampir dengan topik artikel
Mari kita "sorong di bawah CASE" kedua-dua syarat ini:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Dalam kes ini, kami tidak mentakrifkan ELSE
-nilai, iaitu, jika kedua-dua syarat adalah palsu CASE
akan kembali NULL
, yang ditafsirkan sebagai FALSE
Π² WHERE
- syarat.
Contoh ini boleh digabungkan dengan cara lain - secukup rasa dan warna:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: bagaimana [tidak] menulis syarat
Kami menghabiskan dua hari untuk menganalisis sebab pencetus "pelik" pencetus ini - mari lihat sebabnya.
Sumber:
IF( NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" is null or NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid) or NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select to_regclass('"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')::oid)
AND ( OLD."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ" <> NEW."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
OR OLD."Π£Π΄Π°Π»Π΅Π½" <> NEW."Π£Π΄Π°Π»Π΅Π½"
OR OLD."ΠΠ°ΡΠ°" <> NEW."ΠΠ°ΡΠ°"
OR OLD."ΠΡΠ΅ΠΌΡ" <> NEW."ΠΡΠ΅ΠΌΡ"
OR OLD."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»" <> NEW."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»" ) ) THEN ...
Masalah #1: Ketaksamaan tidak mengambil kira NULL
Mari kita anggap bahawa segala-galanya OLD
-bidang penting NULL
. Apa yang akan berlaku?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Dan dari sudut melihat syarat-syarat NULL
bersamaan FALSE
, seperti yang dinyatakan di atas.
keputusan: gunakan operator IS DISTINCT FROM
ROW
-pengendali, membandingkan keseluruhan rekod sekaligus:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Masalah nombor 2: pelaksanaan berbeza bagi fungsi yang sama
Bandingkan dengan:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid)
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select to_regclass('"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')::oid)
Kenapa ada pelaburan tambahan SELECT
? Satu fungsi to_regclass
? Kenapa berbeza...
Mari kita betulkan:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"'::regclass::oid
Masalah #3: keutamaan bool
Mari format sumber:
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅} AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
Oops ... Sebenarnya, ternyata dalam kes kebenaran mana-mana dua syarat pertama, keseluruhan syarat berubah menjadi TRUE
, tidak mengambil kira ketidaksamaan. Dan ini sama sekali bukan yang kami mahukan.
Mari kita betulkan:
(
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅}
) AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
Masalah #4 (kecil): kompleks ATAU keadaan untuk satu medan
Sebenarnya, kami mempunyai masalah di No 3 kerana terdapat tiga syarat. Tetapi bukannya mereka, anda boleh bertahan dengan satu, menggunakan mekanisme coalesce ... IN
:
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')
Begitu juga kita NULL
"tangkap", dan kompleks OR
Anda tidak perlu kecoh dengan kurungan.
Dalam jumlah
Mari kita betulkan apa yang kita dapat:
IF (
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"') AND
(
OLD."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, OLD."Π£Π΄Π°Π»Π΅Π½"
, OLD."ΠΠ°ΡΠ°"
, OLD."ΠΡΠ΅ΠΌΡ"
, OLD."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
) IS DISTINCT FROM (
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, NEW."Π£Π΄Π°Π»Π΅Π½"
, NEW."ΠΠ°ΡΠ°"
, NEW."ΠΡΠ΅ΠΌΡ"
, NEW."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
)
) THEN ...
Dan memandangkan fungsi pencetus ini hanya boleh digunakan dalam UPDATE
pencetus kerana kehadiran OLD/NEW
dalam keadaan peringkat atas, maka keadaan ini secara amnya boleh dibawa keluar WHEN
-keadaan seperti yang ditunjukkan dalam #1...
Sumber: www.habr.com