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: سلسلة OR / AND
SELECT ... WHERE EXISTS(... A) OR EXISTS(... B)
خلاف ذلك ، يمكن الحصول على كلاهما EXISTS
سيكون صحيحا ، ولكن كلاهما سيتم تنفيذه.
ولكن إذا علمنا على وجه اليقين أن أحدها "صحيح" في كثير من الأحيان (أو "خطأ" - من أجل AND
- السلاسل) - هل من الممكن "زيادة أولويتها" بطريقة أو بأخرى حتى لا يتم تنفيذ الثانية مرة أخرى؟
اتضح أنه من الممكن - النهج الحسابي قريب من موضوع المقالة
دعنا فقط "ندفع تحت CASE" كلا الشرطين التاليين:
SELECT ...
WHERE
CASE
WHEN EXISTS(... A) THEN TRUE
WHEN EXISTS(... B) THEN TRUE
END
في هذه الحالة ، لم نحدد ELSE
- القيمة ، أي إذا كان كلا الشرطين خاطئين 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
المشغل ، مقارنة السجلات بأكملها مرة واحدة:
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
( {... неравенства} )
عفوًا ... في الواقع ، اتضح أنه في حالة حقيقة أي من الشرطين الأولين ، تتحول الحالة بأكملها إلى 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 ...
المصدر: www.habr.com