SQL bukan C++, juga bukan JavaScript. Oleh karena itu, evaluasi ekspresi logis berbeda, dan ini sama sekali bukan hal yang sama:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Sambil mengoptimalkan rencana eksekusi kueri PostgreSQL
Oleh karena itu, jika Anda masih ingin mengelola prioritas, Anda perlu melakukannya secara struktural membuat kondisi ini tidak seimbang dengan kondisional
Data dan bekerja dengan mereka adalah dasarnya
#0: RTFM
Mulai
Ketika urutan evaluasi penting, dapat diperbaiki dengan konstruk
CASE
. Misalnya, cara ini untuk menghindari pembagian dengan nol dalam sebuah kalimatWHERE
tidak bisa diandalkan:SELECT ... WHERE x > 0 AND y/x > 1.5;
Opsi aman:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Konstruksi yang digunakan
CASE
melindungi ekspresi dari pengoptimalan, sehingga hanya boleh digunakan jika diperlukan.
#1: kondisi pemicu
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Segalanya tampak terlihat bagus, tapi... Tidak ada yang menjanjikan investasi itu SELECT
tidak akan dieksekusi jika kondisi pertama salah. Perbaiki dengan bersarang IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Sekarang mari kita perhatikan baik-baik - seluruh badan fungsi pemicu ternyata "terbungkus". IF
. Dan ini berarti tidak ada yang menghalangi kami untuk menghapus kondisi ini dari penggunaan prosedur WHEN
-kondisi
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Pendekatan ini memungkinkan Anda menghemat sumber daya server dengan jaminan jika kondisinya salah.
#2: ATAU/DAN rantai
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Jika tidak, dapat diperoleh keduanya EXISTS
akan benar, tapi keduanya akan dieksekusi.
Tetapi jika kita tahu pasti bahwa salah satunya "benar" lebih sering (atau "salah" - untuk AND
-chains) - apakah mungkin untuk "meningkatkan prioritasnya" sehingga yang kedua tidak dieksekusi sekali lagi?
Ternyata itu mungkin - pendekatan algoritme dekat dengan topik artikel
Mari kita "mendorong berdasarkan KASUS" kedua kondisi ini:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Dalam hal ini, kami tidak mendefinisikan ELSE
-nilai, yaitu, jika kedua kondisi salah CASE
akan kembali NULL
, yang diartikan sebagai FALSE
Π² WHERE
- kondisi.
Contoh ini dapat digabungkan dengan cara lain - sesuai selera dan warna:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: bagaimana [tidak] menulis kondisi
Kami menghabiskan dua hari untuk menganalisis alasan pemicu "aneh" dari pemicu ini - mari kita lihat alasannya.
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: Ketimpangan tidak memperhitungkan NULL
Mari kita asumsikan semuanya OLD
-bidang penting NULL
. Apa yang akan terjadi?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
Dan dari sudut pandang mengerjakan kondisi NULL
setara FALSE
, seperti yang disebutkan di atas.
keputusan: menggunakan operator IS DISTINCT FROM
ROW
-operator, membandingkan seluruh catatan sekaligus:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
Masalah nomor 2: implementasi berbeda dari fungsi yang sama
Bandingkan:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid)
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = (select to_regclass('"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')::oid)
Mengapa ada investasi tambahan SELECT
? Sebuah fungsi to_regclass
? Kenapa berbeda...
Mari kita perbaiki:
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"'::regclass::oid
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_" = '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"'::regclass::oid
Masalah #3: bool diutamakan
Mari format sumbernya:
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅} AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
Ups ... Nyatanya, ternyata dalam kasus kebenaran salah satu dari dua syarat pertama, seluruh syarat berubah menjadi TRUE
, dengan mengabaikan ketidaksetaraan. Dan ini sama sekali bukan yang kami inginkan.
Mari kita perbaiki:
(
{... IS NULL} OR
{... ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ} OR
{... ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅}
) AND
( {... Π½Π΅ΡΠ°Π²Π΅Π½ΡΡΠ²Π°} )
Masalah #4 (kecil): kondisi OR kompleks untuk satu bidang
Sebenarnya kami bermasalah di No. 3 justru karena ada tiga syarat. Tapi alih-alih mereka, Anda bisa bertahan dengan satu, menggunakan mekanismenya coalesce ... IN
:
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"')
Kita juga NULL
"menangkap", dan kompleks OR
Anda tidak perlu repot dengan tanda kurung.
Total
Mari kita perbaiki apa yang kita punya:
IF (
coalesce(NEW."ΠΠΎΠΊΡΠΌΠ΅Π½Ρ_"::text, '') IN ('', '"ΠΠΎΠΌΠΏΠ»Π΅ΠΊΡ"', '"ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠΎΠΠ°ΡΠΏΠ»Π°ΡΠ΅"') AND
(
OLD."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, OLD."Π£Π΄Π°Π»Π΅Π½"
, OLD."ΠΠ°ΡΠ°"
, OLD."ΠΡΠ΅ΠΌΡ"
, OLD."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
) IS DISTINCT FROM (
NEW."ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠΠ°ΡΠ°ΠΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΡ"
, NEW."Π£Π΄Π°Π»Π΅Π½"
, NEW."ΠΠ°ΡΠ°"
, NEW."ΠΡΠ΅ΠΌΡ"
, NEW."ΠΠΈΡΠΎΠ‘ΠΎΠ·Π΄Π°Π»"
)
) THEN ...
Dan mengingat bahwa fungsi pemicu ini hanya dapat digunakan di UPDATE
pemicu karena keberadaannya OLD/NEW
dalam kondisi tingkat atas, maka kondisi ini umumnya dapat dibawa keluar WHEN
-kondisi seperti di no 1...
Sumber: www.habr.com