SQL bukan C ++, juga JavaScript. Oleh karena itu, perhitungan ekspresi logis berbeda, dan ini bukan hal yang sama:WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
Dalam proses mengoptimalkan rencana eksekusi kueri, PostgreSQL dapat secara sewenang-wenang "mengatur ulang" kondisi yang setara , tidak menghitung satu pun dari mereka untuk catatan individual, menetapkannya pada kondisi indeks yang digunakan ... Singkatnya, paling mudah untuk mengasumsikan bahwa Anda tidak dapat mengontrol dalam urutan mana Kondisi rekan akan dihitung (dan apakah sama sekali) .Oleh karena itu, jika Anda masih ingin mengontrol prioritas, Anda harus secara struktural membuat kondisi ini tidak merata menggunakan ekspresi kondisional dan operator .Data dan bekerja dengan mereka adalah dasar dari kompleks VLSI kami , sehingga sangat penting bagi kami bahwa operasi pada mereka dilakukan tidak hanya dengan benar, tetapi juga secara efisien. Mari kita lihat contoh spesifik di mana kesalahan perhitungan ekspresi dapat dibuat, dan di mana efisiensinya layak ditingkatkan.# 0: RTFM
Mulai contoh dari dokumentasi :Ketika urutan perhitungan penting, itu bisa diperbaiki menggunakan konstruksi CASE
. Misalnya, cara untuk menghindari pembagian dengan nol dalam kalimat tidak WHERE
dapat 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 dengan cara ini CASE
melindungi ekspresi dari optimisasi, oleh karena itu hanya boleh digunakan jika perlu.
# 1: kondisi dalam pemicu
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
Tampaknya semuanya terlihat baik, tapi ... Tidak ada yang berjanji bahwa yang terlampir SELECT
tidak akan dieksekusi jika kondisi pertama salah. Benar menggunakan bersarangIF
:BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
Sekarang mari kita perhatikan dengan seksama - seluruh tubuh fungsi pemicu ternyata "dibungkus" IF
. Dan ini berarti bahwa tidak ada yang mencegah kita menghapus kondisi ini dari prosedur menggunakan WHEN
-kondisi :BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
Pendekatan ini memungkinkan penghematan sumber daya server yang dijamin dalam kondisi yang salah.# 2: ATAU / DAN rantai
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
Kalau tidak, Anda bisa mendapatkan bahwa keduanya EXISTS
akan "benar", tetapi keduanya akan terpenuhi .Tetapi jika kita tahu pasti bahwa salah satu dari mereka "benar" jauh lebih sering (atau "salah" untuk AND
rantai), apakah mungkin untuk "meningkatkan prioritasnya" sehingga yang kedua tidak dilakukan sekali lagi?Ternyata itu mungkin - pendekatan algoritma dekat dengan topik artikel Antipatterns PostgreSQL: catatan langka akan mencapai bagian tengah GABUNGAN .Mari kita "muncul di bawah CASE" kedua kondisi berikut:SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
Dalam hal ini, kami tidak menentukan ELSE
-value, yaitu, jika kedua kondisi salah , ia CASE
akan kembali NULL
, yang ditafsirkan sebagai FALSE
dalam WHERE
-condition.Contoh ini dapat dikombinasikan 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 menganalisis alasan pemicu "aneh" pemicu ini - mari kita lihat mengapa.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 Mempertimbangkan NULL
Bayangkan semua- OLD
bidang itu penting NULL
. Apa yang akan terjadi?SELECT NULL <> 1 OR NULL <> 2;
Dan dari sudut pandang bekerja, kondisinya NULL
setara FALSE
, seperti yang disebutkan di atas.Solusi : gunakan operator IS DISTINCT FROM
dari ROW
-operator, bandingkan seluruh catatan sekaligus:SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
Masalah nomor 2: implementasi berbeda dari fungsi yang sama
Membandingkan:NEW."_" = (select '""'::regclass::oid)
NEW."_" = (select to_regclass('""')::oid)
Mengapa ada sarang tambahan SELECT
? Bagaimana dengan fungsinya to_regclass
? Dan dengan berbagai cara, mengapa? ..Perbaiki:NEW."_" = '""'::regclass::oid
NEW."_" = '""'::regclass::oid
Masalah # 3: prioritas operasi bool
Format sumber:{... IS NULL} OR
{... } OR
{... } AND
( {... } )
Ups ... Faktanya, ternyata dalam kasus kebenaran salah satu dari dua kondisi pertama, seluruh kondisi berubah menjadi TRUE
, tanpa memperhitungkan ketidaksetaraan. Dan ini sama sekali bukan yang kita inginkan.Memperbaiki:(
{... IS NULL} OR
{... } OR
{... }
) AND
( {... } )
Masalah 4 (kecil): kondisi OR kompleks untuk satu bidang
Sebenarnya, kami punya masalah di No. 3 justru karena ada tiga syarat. Tapi alih-alih, Anda bisa melakukannya, menggunakan mekanisme coalesce ... IN
:coalesce(NEW."_"::text, '') IN ('', '""', '""')
Jadi kita NULL
βtangkapβ, dan komplek OR
dengan kurung tidak harus dipagari.Total
Kami memperbaiki apa yang kami dapatkan:IF (
coalesce(NEW."_"::text, '') IN ('', '""', '""') AND
(
OLD.""
, OLD.""
, OLD.""
, OLD.""
, OLD.""
) IS DISTINCT FROM (
NEW.""
, NEW.""
, NEW.""
, NEW.""
, NEW.""
)
) THEN ...
Dan jika kita memperhitungkan bahwa fungsi pemicu ini hanya dapat digunakan di UPDATE
-trigger karena adanya OLD/NEW
level teratas dalam kondisi, maka kondisi ini secara umum dapat dimasukkan ke dalam WHEN
-kondisi, seperti yang ditunjukkan pada # 1 ...