الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

الرجاء المساعدة العاجلة كيف أقرأ من جدول في Row trigger

مغلق
بدأه aroma_kt في 18 نوفمبر 2006 · 6 رد · 2,650 مشاهدة · في قسم لغةالاجرائات Pl/sql
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله وبركاته:

لو سمحتم أحتاج إلى خبرة الأعضاء المحترفين

أعمل على إنشاء قاعدة بيانات لتخزين إجازات الموظفين

لدي جدول الإجازات تعريفه كالتالي:

CREATE TABLE leave_taken (
emp_id NUMBER(4) NOT NULL
CONSTRAINT leavetaken_empid_fk REFERENCES emp_info (emp_id),
leavetaken_type NOT NULL
CONSTRAINT leavetaken_type_fk REFERENCES leave_types (leavetype_id),
leavetaken_date DATE NOT NULL,
leavetaken_duration NUMBER(4) NOT NULL,
leavetaken_durationsort NUMBER(1) NOT NULL
CONSTRAINT leavetaken_durationsort_fk REFERENCES duration_sorts (durationsort_id),
leavetaken_note VARCHAR2(50),
user_reccreator VARCHAR2(20)
);

ما أحاول عمله هو منع المستخدم من أن يقوم بإدخال إجازة تتداخل مع إجازة موجودة لنفس الموظف

مثلاً:

الموظف ذو الرقم 1 أخذ إجازة بتاريخ الأول من أيلول 2006 مدتها 3 أيام هذا يعني (1و 2 و3 أيلول إجازة)

إذا حاولنا إدخال إجازة لنفس الموظف بتاريخ 2 أيلول 2006 مدتها 1 يوم

يجب عدم السماح بإدخال هذه الإجازة لأنها تتداخل (تتقاطع) مع الإجازة الأولى

لقد أنشأت function أمرر له تاريخي (بداية ونهاية) الإجازة الحالية وتاريخي (بداية ونهاية) الإجازة الجديدة

فيعطيني إن كان هناك تداخل بينهما أم لا كالتالي:

CREATE OR REPLACE FUNCTION LEAVES_CONFLICT (
ODATE1 IN DATE,
ODATE2 IN DATE,
NDATE1 IN DATE,
NDATE2 IN DATE
) RETURN NUMBER 
IS
BEGIN
IF NDATE1 >= ODATE1 AND NDATE2 <= ODATE2 THEN RETURN (1);
ELSIF NDATE1 >= ODATE1 AND NDATE2 >= ODATE2 AND ODATE2 >= NDATE1 AND ODATE2 <= NDATE2 THEN RETURN (1);
ELSIF NDATE1 <= ODATE1 AND NDATE2 <= ODATE2 AND ODATE1 >= NDATE1 AND ODATE1 <= NDATE2 THEN RETURN (1);
ELSIF NDATE1 <= ODATE1 AND NDATE2 >= ODATE2 THEN RETURN (1);
ELSE RETURN (0);
END IF;
END LEAVES_CONFLICT;
/

كما أنشأت function أعطيه تاريخ بدء الإجازة ومدتها: (1 أو 2 أو 365 ..إلخ) ونوع المدة: (دقيقة - ساعة - يوم - شهر - عام)

فيعطيني تاريخ نهايتها كالتالي:

CREATE OR REPLACE FUNCTION CalcEndOfLeave (  
START_DATE IN DATE,  
DURATION IN LEAVE_TAKEN.LEAVETAKEN_DURATION%TYPE,  
DURATION_SORT IN LEAVE_TAKEN.LEAVETAKEN_DURATIONSORT%TYPE)  
RETURN DATE  
IS  
END_DATE DATE;  
BEGIN  
END_DATE :=  
CASE DURATION_SORT  
WHEN 1 THEN (START_DATE + DURATION/60/24)--Minute  
WHEN 2 THEN (START_DATE + DURATION/24)--Hour  
WHEN 3 THEN (START_DATE + DURATION - 1)--Day  
WHEN 4 THEN (START_DATE + DURATION*30 - 1)--Month  
WHEN 5 THEN (START_DATE + DURATION*365 - 1)--Year  
END;  
RETURN (END_DATE);  
END CalcEndOfLeave;  
/

وأنشأت function يعمل query على جدول الإجازات ليرى إن كانت الإجازة (التي أمررها له كباراميتر) تتقاطع مع إجازات سابقة (لنفس الموظف بالطبع)

فإن كان هناك تقاطع يعيد إلي شريط محرفي يحوي (بداية ونهاية) الإجازة التي تتقاطع

لأنني لا أكتفي بإظهار رسالة للمستخدم بأن الإجازة التي يحاول إدخالها تتقاطع بل وأقوم بإظهار هذا الشريط المحرفي للمستخدم

CREATE OR REPLACE FUNCTION CONFLICT_ROW (
P_EMPID IN EMP_INFO.EMP_ID%TYPE,
NEW_LEAVE_START IN DATE,
NEW_LEAVE_END IN DATE,
EXCEPT_LEAVE_START IN DATE)
RETURN VARCHAR2
IS
INVALID_LEAVE VARCHAR2(25);
BEGIN
IF EXCEPT_LEAVE_START IS NULL THEN
SELECT LEAVETAKEN_DATE||' > '||CalcEndOfLeave(LEAVETAKEN_DATE,LEAVETAKEN_DURATION,LEAVETAKEN_DURATIONSORT)
INTO INVALID_LEAVE 
FROM LEAVE_TAKEN 
WHERE EMP_ID = P_EMPID AND LEAVETAKEN_TYPE <> 1
AND LEAVES_CONFLICT(LEAVETAKEN_DATE, CalcEndOfLeave(LEAVETAKEN_DATE,LEAVETAKEN_DURATION,LEAVETAKEN_DURATIONSORT), NEW_LEAVE_START, NEW_LEAVE_END)=1;
ELSE
SELECT LEAVETAKEN_DATE||'>'||CalcEndOfLeave(LEAVETAKEN_DATE,LEAVETAKEN_DURATION,LEAVETAKEN_DURATIONSORT)
INTO INVALID_LEAVE 
FROM LEAVE_TAKEN 
WHERE EMP_ID = P_EMPID AND LEAVETAKEN_TYPE <> 1 AND LEAVETAKEN_DATE <> EXCEPT_LEAVE_START
AND LEAVES_CONFLICT(LEAVETAKEN_DATE, CalcEndOfLeave(LEAVETAKEN_DATE,LEAVETAKEN_DURATION,LEAVETAKEN_DURATIONSORT), NEW_LEAVE_START, NEW_LEAVE_END)=1;
END IF;
RETURN (INVALID_LEAVE);
EXCEPTION
WHEN NO_DATA_FOUND THEN RETURN (NULL);
END CONFLICT_ROW;
/

ثم أنشأت ال trigger الذي يمنع المستخدم من إدخال إجازة تتقاطع مع إجازات سابقة (لنفس الموظف):

CREATE OR REPLACE TRIGGER CHECK_CONFLICT
BEFORE INSERT OR UPDATE ON LEAVE_TAKEN
FOR EACH ROW
WHEN (NEW.LEAVETAKEN_TYPE <> 1) --hour leave
DECLARE
V_INVALID VARCHAR2(25);
BEGIN
IF INSERTING THEN
V_INVALID := CONFLICT_ROW(:NEW.EMP_ID, :NEW.LEAVETAKEN_DATE, CalcEndOfLeave(:NEW.LEAVETAKEN_DATE, :NEW.LEAVETAKEN_DURATION, :NEW.LEAVETAKEN_DURATIONSORT), NULL);
ELSIF UPDATING THEN
V_INVALID := CONFLICT_ROW(:NEW.EMP_ID, :NEW.LEAVETAKEN_DATE, CalcEndOfLeave(:NEW.LEAVETAKEN_DATE, :NEW.LEAVETAKEN_DURATION, :NEW.LEAVETAKEN_DURATIONSORT), :OLD.LEAVETAKEN_DATE);
END IF;
IF V_INVALID IS NOT NULL THEN 
RAISE_APPLICATION_ERROR(-20011, 'This leave can''t be added because it will conflict with this one '||V_INVALID);
END IF;
END;
/

ليس هناك مشكلة عند عملية الinsert لإجازة وإنما عند عملية الupdate فإنه يعطيني رسالة خطأ

ERROR at line 1:
ORA-04091: table LMGR.LEAVE_TAKEN is mutating, trigger/function may not see it
ORA-06512: at "LMGR.CONFLICT_ROW", line 17
ORA-06512: at "LMGR.CHECK_CONFLICT", line 7
ORA-04088: error during execution of trigger 'LMGR.CHECK_CONFLICT'

أعلم سبب المشكلة وهي أنه لا يمكن ل Row Trigger الاستعلام من الجدول في حالة update

لكن كيف يمكنني الالتفاف حول هذه المعضلة ؟؟

#2

يمكنك إنشاء Befor statament حيث يقوم بوضع البيانات التي يحضرها من الجدول في Pl/sql Table الذي تقوم بتعريفه داخل package بصفة متحول عام ... بالتالي في Raw Trigger تستطيع الوصول إليه و إلى بيانات الجدول دون مشكلة mutating Table ...

#3

أشكرك على الرد.

لكن في statement trigger لا يمكن معرفة إجازة أيّ موظف يحاول المستخدم تعديلها لأن :NEW و :OLD يمكن استخدامهما في ال Row trigger فقط

أليس كذلك ؟؟

#4

لا يهمك old و new هنا ..لأن Befor Statment سيقوم بملأ الجدول pl.sql بالبيانات دون الأخذ بعين الإعتبار ما يريد المستخدم تعديله .... ما يقوم به for each row هو أخذ البيانات من الجدول pl/sql ....

#5

إذاً أنت تقصد:

أن يحتوي جدول plsql على جميع السجلات الموجودة في جدول الإجازات

(وليس التي تخص الموظف الذي يحاول المستخدم تعديل إجازته)

لكن أليس هذا عبئاً كبيراً ؟ ألا يوجد طريقة أسهل

أنا كنت أفكر أنني ربما لو غيرت طريقة الاستعلام من select إلى شيء آخر قد ينجح الاستعلام بدون رسالة mutating table ؟؟!!

تم تعديل هذه المشاركة بواسطة aroma_kt في 19 نوفمبر 2006 في 12:01

#6
اقتباس
أنا كنت أفكر أنني ربما لو غيرت طريقة الاستعلام من select إلى شيء آخر قد ينجح الاستعلام بدون رسالة mutating table ؟؟!!

و كيف ستقوم بفعل ذلك ؟؟؟ طبعاً لا يوجد بديل عن select ...

إلا إذا قررت تغيير بنية جداولك و خصوصاً جدول الإجازات بأن تحاول أن تقسمه إلى قسمين و تربط بينهما ...

ما رأيك بإضافة check Constraint لتحقيق ما تريد ؟؟

1
#7

آسفة، أخطأت التعبير لم أقصد عدم استخدام select نهائياً

ولكن كنت أقصد تغيير أحد أجزائها ال where مثلاً أو باستخدام Cursors

لكن إن كان لديك طريقة باستخدام ال check constraint

أرجو أن تفيدني بها ، لو تكرمت

تم تعديل هذه المشاركة بواسطة aroma_kt في 19 نوفمبر 2006 في 12:58

هذا الموضوع مغلق.

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…