الانتقال إلى المحتوى

دالة تاريخ


eehab_said

Recommended Posts

الأخ الكريم ، قبل أي شيء يجب عليك معرفة محتويات التاريخين ... يعني بيتكون من أي شهور ( من البداية الي النهاية ) ... وتدرج كل هذه الشهور في جدول مستقل كما يلي :

CREATE TABLE TMP_MONTHS
(
 DAT  DATE;
)
/

DECLARE
  ID_START_DATE  CONSTANT DATE := TO_DATE('24/9/1981','DD/MM/YYYY');
  ID_END_DATE	 CONSTANT DATE := TO_DATE('24/9/2008','DD/MM/YYYY');
  ID_DATE   DATE;
BEGIN
  ID_DATE := ID_START_DATE;
  WHILE ID_DATE <= ID_END_DATE
  LOOP
 ID_DATE := ID_DATE + 30.4375;
 INSERT INTO TMP_MONTHS VALUES(TO_DATE(TO_CHAR(ID_DATE,'YYYY/MM') || '1' ,'YYYY/MM/DD'));
  END LOOP;
END;



الآن أنت لديك جدول TEMP يحتوي علي كافة الشهور التي يحتوي عليها ما بين تاريخين ... يجب عليك الآن أن تمتلك جدول آخر أو عارض ليتضمن كافة أيام الشهر الممكنة كما يلي :

CREATE TABLE DAYS
(
 D  VARCHAR2(2);
)
/
DECLARE
 X  NUMBER;
BEGIN
 FOR X IN 1 .. 31
 LOOP
INSERT INTO DAYS VALUES(X);
 END LOOP;
END;
/



السؤال ، لماذا لم يتم إدراج الأيام في جدول TMP_MONTHS علي الفور ... السبب لأن في هذه الحالة لو كان عدد السنوات 10 أو 20 أو 30 أو .... الي آخرة هيكون عملية الـ LOOP بطيئة بالزات أن كل سنة هتكون مكونة من 365 أو 366 يوم ... ولكن فكرة أنك تقم بإدراج جدول وسيط يدعي DAYS تدرج فية كل أيام الشهر دة في حد ذاتة هيخلي عملية الـ LOOP أقل وسرعة عالية نظراً لأن السنة في هذه الحالة ستحتوي علي 12 شهر يعني إجراء دوارة LOOP بمقدار 12 مرة وليس 365 أو 366 لكل سنة.

المهم

طريقة الدمج بين الشهر واليوم :

1- إنشاء FUNCTION يدعي IS_DATE لهدف أنه ليس من الشرط أن يكون كل شهر يحتوي علي 31 يوم :



 
CREATE OR REPLACE FUNCTION IS_DATE(IN_TEXT VARCHAR2) RETURN NUMBER
AS
 ID_DATE DATE;
BEGIN
 ID_DATE := TO_DATE(IN_TEXT,'YYYY/MM/DD');
 RETURN 1;
EXCEPTION
 WHEN OTHERS THEN
RETURN 0;
END;
/




بعد ذلك تنشأ VIEW لجلب كل أيام الشهور بل السنوات كما يلي :

CREATE OR REPLACE LIST_ALL_DAYS
AS
(
 SELECT TO_DATE(TO_CHAR(TMP_MONTHS.DAT,'YYYY/MM/DD') || DAYS.D,'YYYY/MM/DD') AS DAY
 FROM TMP_MONTHS, DAYS
 WHERE IS_DATE(TO_DATE(TO_CHAR(TMP_MONTHS.DAT,'YYYY/MM/DD') || DAYS.D,'YYYY/MM/DD')) = 1
)
/



بكدة هيكون لديك جدول كامل يحتوي علي كل الأيام المطلوبة للسنوات ... ولكن أنتبه يجب عليك إجراء تحديث دوري علي جدول TMP_MONTHS نظراً لتغير القيمة المدخلة كل مرة .



الآن إليك إجابة سؤالك الخاص ، كيف يمكننا إيجاد عدد أيام بين تاريخين دون إحتساب يومي الجمعة أو السبت ... أنظر الكود :

CREATE OR REPLACE FUNCTION GET_COUNT_DAYS(IN_START DATE, IN_END DATE,) RETURN NUMBER
AS
 ID_DATE DATE;
 ID_RETURN NUMBER;
BEGIN
 --1: تحديث جدول الشهور أولاً.
 ID_DATE := ID_START_DATE;
 WHILE ID_DATE <= ID_END_DATE
 LOOP
ID_DATE := ID_DATE + 30.4375;
INSERT INTO TMP_MONTHS VALUES(TO_DATE(TO_CHAR(ID_DATE,'YYYY/MM') 
		 || '1' ,'YYYY/MM/DD'));
 END LOOP;

 --2: إيجاد عدد الأيام المناسبة دون يومي السبت والجمعة.
 SELECT COUNT(*)
 INTO ID_RETURN
 FROM LIST_ALL_DAYS
 WHERE LIST_ALL_DAYS.DAY BETWEEN IN_START AND IN_END AND TO_CHAR
		(LIST_ALL_DAYS.DAY,'D') IN NOT (1,7)

 REUTN ID_RETURN;
END;
/






بالتوفيق

رابط هذا التعليق
شارك

الأخ الكريم ( أحمد يحيي ) أشكرك كل الشكر علي هذا الـ Function الأكثر من روعة ...

بس أسمح لي بأن أوضح بعض الأمور

قاعدة البيانات عاملة زي معالج الحاسب Processor ... وكما تعلم أن المعالج لا يفهم سوي الإشارات الرقمية الثنائية ( لغة آلة ) ... فكلما أقتربت من لغة الآلة كلما كانت العملية أسرع بكثير ... بس هيبقي فية صعوبة التصميم والبرمجة ... لكن لو أبتعدت عن لغة الآلة يعني مثلاً أستخدمت لغة برمجة مثل الفيجوال 6.0 أو الدوت نيت فهتكون العملية سهلة جداً بس هيكون أبطأ نسبياً .


تخيل حضرتك أنك ستصنع Function يعمل Loop علي أساس 25 سنة حضور وإنصارف لموظف محدد ... وكل سنة 365.25 يوم يعني ( 365.25 × 12 = 4383 ) ... في كل مرة سيقوم بإجراء الـ Loop ويؤدي الي توقف مؤقت فترة ( وذلك يرجع الي عدد العمليات المدرجة داخل جملة الـ Loop ) ... أنظر الي العملية البسيطة التالية :

Set ServerOutput On
Declare
 X  Number;
 Y  Number;
Begin
 DBMS_Output.Put_Line(To_Char(SysDate,'HH24:MI:SS'));
 For X In 1 .. 4383
 Loop
Select Count(*) Into Y From Tab;
 End Loop;
 DBMS_Output.Put_Line(To_Char(SysDate,'HH24:MI:SS'));
End;
/



رغم أنها عملية بسيطة ولكنها أخذت مني علي سرعة معالج 256 ورامات 512 بمقدار 12 ثواني ... ولكن ماذا لو كان العميل لدية حاسب أضعف ... عموماً الثانية لو كانت أقل من الملي ثانية بالطبع هيكون أفضل بكثير .

لكن لو صنعنا Loop بسيط وبعد ذلك إستخدمنا جمل الـ SQL هيكون أسرع


لذلك أقول أن كلما أقتربنا من الـ SQL هيكون العملية أسرع بكثير من الـ PL خصوصاً لو كانت العملية تتطلب الي عرض أكثر من سجل ... وكل سجل يحتوي علي 3 أو 4 أو 5 أو -------------- ؟ حقول ... وكل حقل يتضمن Function يأخز وقت ثقيل بسبب جملة Loop .

أنا لا أقول أن الـ PL/SQL ضعيف ... ولا أقل أن الـ Loop غير مطلوب ... ولكنني أقول بأن كل صح له الأصح منه

علي فكرة أخي أنا والله جربت هذه الفكرة من قبل ... بس عجبتني الفكرة التي أدرجتها لأنها فعلاً بسيطة وواضحة وفي نفس الوقت أسرع بكثير ... مجرد تنفذ مرة واحدة وبعد كدة تجلب الي أنت عايزة .






شكراً ، وبالتوفيق للجميع ،

رابط هذا التعليق
شارك

كلامك صحيح يا أخ مصطفى وأنا معاك. عشان كده إحنا ممكن نعمل حاجه ظريفه زياده على الفانكشن دي:

نستخدم تقنية ال forall ونعمل temp table ثم نقوم بعملية إدخال للأيام غير الجمعه والسبت وبعد كده نعمل count للسجلات وتقريباًَ ده هايكون كويس جداً لعملية ال performance .

طبعاً لابد وأن نضع في الحسبان الآداء .

رابط هذا التعليق
شارك

انضم إلى المناقشة

يمكنك المشاركة الآن والتسجيل لاحقاً. إذا كان لديك حساب, سجل دخولك الآن لتقوم بالمشاركة من خلال حسابك.

زائر
أضف رد على هذا الموضوع...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   تمت استعادة المحتوى السابق الخاص بك.   مسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

جاري التحميل
×
×
  • أضف...

برجاء الإنتباه

بإستخدامك للموقع فأنت تتعهد بالموافقة على هذه البنود: سياسة الخصوصية