SQL ไม่ใช่ C++ และไม่ใช่ JavaScript ดังนั้น การประเมินนิพจน์เชิงตรรกะจึงแตกต่างกัน และนี่ไม่ใช่เรื่องเดียวกันเลย:
WHERE fncondX() AND fncondY()
= fncondX() && fncondY()
ในขณะที่ปรับแผนการดำเนินการของคิวรี PostgreSQL ให้เหมาะสม
ดังนั้น หากคุณยังต้องการจัดการลำดับความสำคัญ คุณต้องทำโครงสร้าง ทำให้เงื่อนไขเหล่านี้ไม่เท่ากัน โดยมีเงื่อนไข
ข้อมูลและการทำงานร่วมกับพวกเขาเป็นพื้นฐาน
#0: RTFM
เริ่มต้น
เมื่อลำดับการประเมินมีความสำคัญ ก็สามารถแก้ไขได้ด้วยโครงสร้าง
CASE
. ตัวอย่างเช่น วิธีนี้เพื่อหลีกเลี่ยงการหารด้วยศูนย์ในประโยคWHERE
ไม่น่าเชื่อถือ:SELECT ... WHERE x > 0 AND y/x > 1.5;
ตัวเลือกที่ปลอดภัย:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
ที่ใช้ก่อสร้าง
CASE
ปกป้องนิพจน์จากการเพิ่มประสิทธิภาพ ดังนั้นควรใช้เมื่อจำเป็นเท่านั้น
#1: เงื่อนไขทริกเกอร์
BEGIN
IF cond(NEW.fld) AND EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
ทุกอย่างเหมือนจะดูดี แต่... ไม่มีใครสัญญาว่าลงทุนไป SELECT
จะไม่ดำเนินการหากเงื่อนไขแรกเป็นเท็จ แก้ด้วย ซ้อนกัน IF
:
BEGIN
IF cond(NEW.fld) THEN
IF EXISTS(SELECT ...) THEN
...
END IF;
END IF;
RETURN NEW;
END;
ทีนี้มาดูอย่างระมัดระวัง - ฟังก์ชั่นทริกเกอร์ทั้งหมดกลายเป็น "ห่อ" เข้าไป IF
. และนั่นหมายความว่าไม่มีสิ่งใดขัดขวางเราจากการลบเงื่อนไขนี้ออกจากขั้นตอนการใช้งาน WHEN
-เงื่อนไข
BEGIN
IF EXISTS(SELECT ...) THEN
...
END IF;
RETURN NEW;
END;
...
CREATE TRIGGER ...
WHEN cond(NEW.fld);
วิธีการนี้ช่วยให้คุณประหยัดทรัพยากรเซิร์ฟเวอร์ด้วยการรับประกันหากเงื่อนไขเป็นเท็จ
#2: หรือ/และโซ่
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
มิฉะนั้นสามารถรับได้ทั้งสองอย่าง EXISTS
จะเป็นจริงแต่ ทั้งสองจะถูกประหารชีวิต.
แต่ถ้าเรารู้แน่นอนว่าหนึ่งในนั้น "จริง" บ่อยกว่ามาก (หรือ "เท็จ" - สำหรับ AND
-chains) - เป็นไปได้ไหมที่จะ "เพิ่มลำดับความสำคัญ" เพื่อให้อันที่สองไม่ถูกดำเนินการอีกครั้ง?
ปรากฎว่าเป็นไปได้ - วิธีการอัลกอริทึมนั้นใกล้เคียงกับหัวข้อของบทความ
ลอง "ดันภายใต้ CASE" ทั้งสองเงื่อนไขนี้:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
ในกรณีนี้ เราไม่ได้กำหนด ELSE
-value นั่นคือถ้าทั้งสองเงื่อนไขเป็นเท็จ CASE
จะกลับมา NULL
ซึ่งตีความได้ว่า FALSE
в WHERE
- เงื่อนไข.
ตัวอย่างนี้สามารถรวมกันในลักษณะอื่น - เพื่อลิ้มรสและสี:
SELECT ...
WHERE
CASE
WHEN NOT EXISTS(... A) THEN EXISTS(... B)
ELSE TRUE
END
#3: วิธี [ไม่] เขียนเงื่อนไข
เราใช้เวลาสองวันในการวิเคราะห์สาเหตุของการทริกเกอร์ที่ "แปลก" ของทริกเกอร์นี้ มาดูกันว่าทำไม
แหล่งที่มา:
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 ...
ปัญหา #1: ความไม่เท่าเทียมกันไม่ได้คำนึงถึงค่า NULL
สมมติว่าทุกอย่าง OLD
- ฟิลด์มีความสำคัญ NULL
. อะไรจะเกิดขึ้น?
SELECT NULL <> 1 OR NULL <> 2;
-- NULL
และจากมุมมองของการทำงานตามเงื่อนไข NULL
เทียบเท่า FALSE
ดังกล่าวข้างต้น
การตัดสิน: ใช้ตัวดำเนินการ IS DISTINCT FROM
ROW
-operator เปรียบเทียบบันทึกทั้งหมดในครั้งเดียว:
SELECT (NULL, NULL) IS DISTINCT FROM (1, 2);
-- TRUE
ปัญหาหมายเลข 2: การใช้งานที่แตกต่างกันของฟังก์ชันเดียวกัน
ลองเปรียบเทียบ:
NEW."Документ_" = (select '"Комплект"'::regclass::oid)
NEW."Документ_" = (select to_regclass('"ДокументПоЗарплате"')::oid)
ทำไมถึงมีการลงทุนพิเศษ SELECT
? ฟังก์ชั่น to_regclass
? ทำไมถึงแตกต่าง...
มาแก้ไขกัน:
NEW."Документ_" = '"Комплект"'::regclass::oid
NEW."Документ_" = '"ДокументПоЗарплате"'::regclass::oid
ปัญหา # 3: ลำดับความสำคัญแบบบูล
มาจัดรูปแบบแหล่งที่มา:
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате} AND
( {... неравенства} )
อ๊ะ... ความจริงแล้วกลายเป็นว่าในกรณีที่ความจริงข้อใดข้อหนึ่งใน XNUMX ข้อแรก เงื่อนไขทั้งหมดกลายเป็น TRUE
โดยไม่คำนึงถึงความไม่เท่าเทียมกัน และนี่ไม่ใช่สิ่งที่เราต้องการเลย
มาแก้ไขกัน:
(
{... IS NULL} OR
{... Комплект} OR
{... ДокументПоЗарплате}
) AND
( {... неравенства} )
ปัญหา #4 (เล็ก): เงื่อนไข OR ที่ซับซ้อนสำหรับหนึ่งฟิลด์
จริงๆ แล้วเรามีปัญหาในข้อ 3 เพราะมีสามเงื่อนไข แต่แทนที่จะทำได้โดยใช้กลไก coalesce ... IN
:
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"')
เราก็เช่นกัน NULL
"จับ" และซับซ้อน OR
คุณไม่ต้องยุ่งยากกับวงเล็บ
เบ็ดเสร็จ
มาแก้ไขสิ่งที่เราได้รับ:
IF (
coalesce(NEW."Документ_"::text, '') IN ('', '"Комплект"', '"ДокументПоЗарплате"') AND
(
OLD."ДокументНашаОрганизация"
, OLD."Удален"
, OLD."Дата"
, OLD."Время"
, OLD."ЛицоСоздал"
) IS DISTINCT FROM (
NEW."ДокументНашаОрганизация"
, NEW."Удален"
, NEW."Дата"
, NEW."Время"
, NEW."ЛицоСоздал"
)
) THEN ...
และเนื่องจากฟังก์ชันทริกเกอร์นี้สามารถใช้ได้เฉพาะใน UPDATE
ทริกเกอร์เนื่องจากการมีอยู่ OLD/NEW
ในเงื่อนไขระดับบน โดยทั่วไปแล้วเงื่อนไขนี้สามารถนำออกไปได้ WHEN
-สภาพตามรูป#1...
ที่มา: will.com