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

استشارة في تسريع الاتصال مع Sql Server

بدأه Like Delphi في 24 فبراير 2009 · 16 رد · 4,928 مشاهدة · في لغة Delphi
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بســم الله الـرحمــن الرحيــم

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

إخواني الكرام:

برنامجي يتصل عن طريق الشبكة بقاعدة بيانات SQL Server 2005 enterprise RTM, باسم user وكلمة مرور

وباستخدام محرك Sql native client و دلفي 7.

و البرنامج موزع على أجهزة المستخدمين على الشبكة والكل يستعمل البرنامج الذي يدخل على قاعدة البيانات باستخدام نفس الـ user للكل.

و البرنامج يعمل و الحمد لله.

هذه فكرة واضحة عن الوضع, و الذي أود السؤال عنه هو أنني أتصل أو كل برنامج يتصل بقاعدة البيانات

يقوم بالاتصال بـ 10 جدوال مرة واحدة يعني adotable1.active:=true و adotable2.active:=true. و هكذا حتى النتهاء من الجداول المطلوب التعامل معها.

مشكلتي هي في سرعة الاتصال فهي بطيئة جداً.

حيث أنه قمت بعمل اختبار بسيط و اتصلت بكل جدول على حدىو فتبين لدي أنه هناك ثلاث جداول فقط هي التي يكون الاتصال معها بطيء جداً.

حيث يحتوي الواحد منها على سجلات يتراوح عددها بين 30000 إلى 60000 سجل.

علماً أن هذه البيانات تنمو باستمرار و مع نموها يزداد البطئ في الاتصال.

ماذا يمكن أن تنصحونني لحل هذه المشكلة؟

وجزاكم الله خيراً

قال تعالى: ((وَمَا أَصَابََكُمْ مِنْ مُصِيبَةٍ فَبمَا كَسَبَتْ أَيْدِيكُمْ وَ يَعْفُو عَنْ كَثِيرٍ)) :.:.: الشورى: الآية 30.

IN ALLAH WE TRUST

:.:.:.:.:.:.:.:.:.:.:.:.:.:

#2

السلام عليكم...

لا أدري إن كان هناك برمجيات (Software) تقوم بذلك أو إن كانت هناك إعدادات في SQL Server لها علاقة بذلك. لكن من ناحية البرمجة فأنا شخصياً أتبع النقاط التالية:

1. استخدام الـ ADOQuery بدل الـ ADOTable للوصول فقط للحقول (Fields) و السجلات (Records) التي أحتاجها:

  1. SELECT field1, field2, ... FROM table1 WHERE (...)
  2.  

2. عدم فتح أي Query إلا عند الحاجة إليه (ADOQuery1.Open) و إغلاقه عند عدم الحاجة إليه (ADOQuery1.Close) لتجنب ازدحام الشبكة و الاستهلاك الزائد للذاكرة.

3. عند معالجة البيانات (إضافة، تعديل، حذف) لا يتم فتح جدول بأكمله (مع ما يترتب عنه من جلب كافة السجلات) و استخدام Append أو Delete أوEdit ، بل يتم ذلك عن طريق استخدام ADOCommand و إرسال جملة INSERT أو DELETE أو UPDATE إلى قاعدة البيانات.

3. بعض الاستعلامات المعقدة أقوم بإنشائها داخل الـ SQL Server نفسه على هيئة Views.

* ربما يفيدك و يفيدنا بقية الإخوة بالمزيد من المعلومات حول هذا الأمر.

نرجو الاستفادة و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#3

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

أخي najy_zl

جزاك الله خيراً على هذه النصائح.

قمت باستثناء تفعيل الـ adotables الخاصة بالجداول الكبيرة عند بداية التشغيل, طبعاً السرعة أصبحت طبيعية, و بقية الجداول لا أجد ضير في التعامل المباشر معها وذلك

لقلة البيانات المخزنة فيها, فليس عندي مشكلة فيها.

الآن أريد أن أطبق طريقة التعامل مع البيانات باستخدام الـ adocommand و تخزين البيانات في القاعدة و هذه هي الطريقة الصحيحة كما قلت لحماية موارد الشبكة.

مشكلتي الآن هي أن الجداول الأربعة التي سبب المشكلة, هي:

qmaster_new وهو جدول رئيسي

qmaster_tcoded_details وهو جدول فرعي مرتبط بعلاقة رأس بأطراف مع أبوه qmaster_new

qmaster_p_master وهو جدول فرعي أيضأ مرتبط بعلاقة رأس بأطراف مع الأب qmaster_new, وبنفس الوق هو جدول رئيسي للجدول التالي.

qmaster_p_details وهو جدول فرعي مرتبط بعلاقة رأس بأطراف مع أبوه qmaster_P_master.

هذه هي تقسيمة هذه الجدول و ارتباطها مع بعض في قاعدة البياناتو أيضاً كنت قد ربطت الـ adotables التابعة لها على الفورم عن طريق خاصية mastersource و master fields.

كيف يمكنني تخزين بيانات في هذه الجداول عن طريق الـ adocommand؟

وجزاكم الله خيراً

قال تعالى: ((وَمَا أَصَابََكُمْ مِنْ مُصِيبَةٍ فَبمَا كَسَبَتْ أَيْدِيكُمْ وَ يَعْفُو عَنْ كَثِيرٍ)) :.:.: الشورى: الآية 30.

IN ALLAH WE TRUST

:.:.:.:.:.:.:.:.:.:.:.:.:.:

#4

السلام عليكم...

أضف المكون المكون ADOSmmand من صفحة ADO إلى الـ Form التي ستتم فيها العملية. اضبط الخاصية Connection على اسم الـ ADOConnection الذي تستخدمه في برنامجك. إذا لم تكن تستخدم الـ ADOConnection فيجب إضافة واحد إلى النافذة الرئيسية (التي تبقة مفتوحة طوال عمل البرنامج) و اضبط الخاصية ConnectionString للـ ADOConnection كما تفعل مع الجداول. اضبط الخاصية LoginPrompt على Fale.

* بالنسبة إلى إضافة السجلات نستعمل عبارة INSERT (و هي من عبارات لغة SQL) مع الملاحظات التالية:

1. إذا كانت الإضافة تتم على جدول واحد أو جداول ليست مرتبطة ببعضها، أي لا توجد بينها علاقات ربط فليس مهماً ترتيب استخدام الجداول، و مع ذلك فإذا كنا نتعامل مع أكثر من جدول واحد فيستحسن دائماً تغليف عمليات الإضافة داخل صفقة (Transaction). إن استعمال الـ Transaction في مثل هذه الحالات يضمن لك تكامل البيانات (Data Integrity) لأن الـ Transaction يضمن لك أن كل العمليات التي تقوم بها ضمنه سيتم حفظها جميعاً أو إلغاؤها جميعاً.

طريقة استعمال الـ Transaction تكون كالتالي - نفرض أننا أسمينا الـ ADOConnection باسم cnnConnect و هو موجود في النافذة الرئيسية المسماة FMainWin

  1. try
  2. // فتح صفقة
  3. FMainWin.cnnConnect.BeginTrans
  4.  
  5. // ...
  6. // عمليات الإضافة
  7. // أو عمليات التعديل
  8. // أو عمليات الحذف
  9. // أو مزيج من تلك العمليات
  10. // ...
  11.  
  12. // إذا سار كل شيء على ما يرام نقوم بإتمام الصفقة و إغلاقها
  13. if FMainWin.cnnConnect.InTransaction then FMainWin.cnnConnect.CommitTrans;
  14.  
  15. // قد تكون لديك إجراءات أخرى إضافية هنا بعد حفظ البيانات
  16.  
  17. // ...
  18.  
  19. except
  20. on E: Exception do begin
  21. // في حالة حدوث خطأ نقوم بإلغاء الصفقة و إغلاقها
  22. if FMainWin.cnnConnect.InTransaction then
  23. FMainWin.cnnConnect.RollbackTrans;
  24. // نعرض على المستخدم رسالة بحدوث الخطأ
  25. MsgText := 'حدث خطأ أثناء محاولة حفظ البيانات'
  26. + #13#10 + E.Message;
  27. ShowMessage(MsgText);
  28. end;
  29. end;
  30.  

مع ملاحظة أن MsgText المذكور في نهاية الكود هو متغير من النوع string.

ملاحظات:

* الـ Transaction يتم باستخدام الـ ADOConnection.

* يتم فتح أو بدء الـ Transaction باستعمال المنهج BeginTrans.

* يتم إتمام الـ Transaction (أي حفظ كافة العمليات التي تمت خلاله) باستعمال المنهج CommitTrans.

* يتم إلغاء الـ Transaction (أي إلغاء كافة العمليات التي تمت خلاله) باستعمال المنهج RollbackTrans.

* يتم تلقائياً إغلاق أو إنهاء الـ Transaction عند استعمال CommitTrans أو RollbackTrans.

نأتي إلى عمليات الإضافة نفسها:

يتم ذلك باستخدام جملة INSERT كما قلنا سابقاً، و لها الصيغة العامة التالية:

  1. INSERT INTO tablename (field1, field2, field3, ...) VALUES (value1, value2, value3, ...)
  2.  

حيث:

tablename اسم الجدول.

field1, field2, field3, ... أسماء الحقول في الجدول، و يجب وضعها ضمن قوسين.

value1, value2, value3, ... القيم المطلوب حفظها في تلك الحقول، و يجب وضعها ضمن قوسين.

هناك تفاصيل كثيرة حول هذه الجملة، سنحاول ذكر أهمها (إذا كان لديك SQL Server 2000 فانظر ملف التعليمات المرفق معه - Books Online - فهو مفيد جداً و يحتوي على كافة التفاصيل مع الأمثلة):

* يمكن إهمال قائمة الحقول بأكملها إذا كنا سنوفر قيماً لكافة الحقول ضمن عبارة VALUES (باستثناء حقل الـ IDENTITY). في هذه الحالة يجب أن يكون ترتيب و أنواع البيانات ضمن عبارة VALUES مطابقاً لترتيب و أنواع الحقول في جدول قاعدة البيانات. القيم يمكن أن تكون رقمية (تكتب كما هي) أو نصية (تكتب بين علامات اقتباس فردية - غالباً ستستعمل الدالة QuotedStr) أو تاريخ (و وقت) (تكتب بين علامات اقتباس فردية - غالباً ستستعمل الدالة QuotedStr)، أو كلمة NULL لترك الحقل فارغاً إذا كان يسمح بذلك (AllowNulls)، أو كلمة DEFAULT لإدراج القيمة الافتراضية إذا كانت له قيمة افتراضية (default value).

* قائمة الحقول لا يمكن أن تضم اسم حقل إذا كان IDENTITY (ترقيم تلقائي) لأن الـ Server سيحدد له قيمته الجديدة تلقائياً. هناك استثناء لهذه القاعدة لكن شرحه يطول قليلاً!

* يمكن عدم ذكر بعض أسماء الحقول ضمن القائمة، و في هذه الحالة يجب أن يكون للحقل غير المذكور إحدى الخصائص التالية:

= أن يقبل القيم الفارغة (Nulls)، أي أن الخاصية AllowNulls للحقل (عند تصميم الجدول) تكون مضبوطة على True.

= أن تكون له قيمة افتراضية (Default value) عند تصميم الجدول.

= أن تكون الخاصية IDENTITY للحقل مضبوطة على Yes (حقل ترقيم تلقائي).

مثال للتوضيح:

نفرض أن لدينا جدول باسم Customers به الحقول التالية:

الحقل ID: رقم الموظف، نوعه int و هو حقل IDENTITY.

الحقل CustName: اسم الموظف، نوعه nvarchar. لا يسمح بالقيم الفارغة.

الحقل HireDate: تاريخ التوظيف، نوعه datetime. لا يسمح بالقيم الفارغة و قيمته الافتراضية هي ()getdate (الدالة getdate هي من دوال SQLServer و تستعمل للحصول على التاريخ الحالي).

الحقل Salary: المرتب، نوع money. لا يسمح بالقيم الفارغة.

الحقل Notes: ملاحظات، نوعه ntext و يسمح بالقيم الفارغة.

لدينا Form عليها الخانات التالية:

Edit1 لاسم الموظف.

DateTimePicker1 لتاريخ التوظيف.

Edit2 للمرتب.

Memo1 للملاحظات.

كما تحتوي النافذة على ADOCommand باسم ADOCommand1

الكود:

  1. var
  2. SQL, SalaryDate: string;
  3. begin
  4. // ...
  5.  
  6. SalaryDate := FormatDateTime('mm/dd/yyyy', DateTimePicker1.Date);
  7.  
  8. SQL := 'INSERT INTO Customers (CustName, HireDate, Salary, Notes) VALUES ('
  9. + QuotedStr(Edit1.Text)
  10. + ', ' + QuotedStr(SalaryDate)
  11. + ', ' + Edit2.Text;
  12. if Trim(Memo1.Text) = '' then
  13. SQL := SQL + ', NULL)'
  14. else
  15. SQL := SQL + ', ' + QuotedStr(Memo1.Text) + ')';
  16.  
  17. ADOCommand1.CommandText := SQL;
  18. ADOCommand1.Execute;
  19.  
  20. // ...
  21.  
  22. end;
  23.  

على أية حال ما زال هناك بعض التفاصيل لكنني أعتذر هذه المرة عن الإكمال بسبب ضيق الوقت و انشغالي بأمور أخرى، على أن نكمل في مرة قادمة إن شاء الله تعالى.

نرجو الاستفادة و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#5

تعديل طفيف على الاخ و الاستاذ ناجي لاني احب انشاء الشئ ثم تحطيمه من الذاكرة

 

 PROCEDURE TForm1[color= #339933;].Button1Click[color= #009900;](Sender[color= #339933;]: TObject[color= #009900;])[color= #339933;];

 [color= #000000; font-weight: bold;]VAR

     ADOCommand[color= #339933;]:TADOCommand[color= #339933;];

     SQL[color= #339933;], SalaryDate[color= #339933;]: string[color= #339933;];

 

 BEGIN

     ADOCommand[color= #339933;]:=TADOCommand[color= #339933;].Create[color= #009900;](nil[color= #009900;])[color= #339933;];

     TRY

       ADOCommand[color= #339933;].Connection[color= #339933;]:=ADOConnection1[color= #339933;];

       SalaryDate [color= #339933;]:= FormatDateTime[color= #009900;]([color= #0000ff;]'mm/dd/yyyy'[color= #339933;], DateTimePicker1[color= #339933;].[color= #990000;]Date[color= #009900;])[color= #339933;];

       SQL [color= #339933;]:=format[color= #009900;]([color= #0000ff;]'INSERT INTO Customers (CustName, HireDate, Salary, Notes) VALUES (%S,%S,%S
'[color= #339933;],

                      [color= #009900;][QuotedStr[color= #009900;](Edit1[color= #339933;].Text[color= #009900;])[color= #339933;],QuotedStr[color= #009900;](SalaryDate[color= #009900;])[color= #339933;],Edit2[color= #339933;].Text[color= #009900;]][color= #009900;])[color= #339933;];

       [color= #b1b100;]if

           [color= #990000;]Trim[color= #009900;](Memo1[color= #339933;].Text[color= #009900;]) [color= #339933;]= [color= #0000ff;]'' then SQL [color= #339933;]:= SQL [color= #339933;]+ [color= #0000ff;]', NULL)'

       [color= #b1b100;]else

           SQL [color= #339933;]:= SQL [color= #339933;]+ [color= #0000ff;]', ' [color= #339933;]+ QuotedStr[color= #009900;](Memo1[color= #339933;].Text[color= #009900;]) [color= #339933;]+ [color= #0000ff;]')'[color= #339933;];

 

      ADOCommand[color= #339933;].CommandText [color= #339933;]:= SQL[color= #339933;];

      TRY

        ADOCommand[color= #339933;].Execute[color= #339933;];[color= #666666; font-style: italic;]// ...

     [color= #990000;]end[color= #339933;];

     EXCEPT ON E[color= #339933;]:exception [color= #b1b100;]DO

     [color= #666666; font-style: italic;]// LOG('');

     [color= #990000;]END[color= #339933;];

     FINALLY ADOCommand[color= #339933;].Free[color= #339933;];

     [color= #990000;]END[color= #339933;];

 [color= #990000;]END[color= #339933;];

[color= #990000;]END[color= #339933;];

كما افضل استعمال الاجراءات المخزنة في قاعدة البيانات فهي تسرع عمل تعليمات لاسكل

ولا تنسى NOLOCK عند الحاحة

 

PROCEDURE TForm1[color= #339933;].Button1Click[color= #009900;](Sender[color= #339933;]: TObject[color= #009900;])[color= #339933;];

[color= #000000; font-weight: bold;]VAR

      ADOStoredProc[color= #339933;]:TADOStoredProc[color= #339933;];

BEGIN

      ADOStoredProc[color= #339933;]:=TADOStoredProc[color= #339933;].Create[color= #009900;](nil[color= #009900;])[color= #339933;];

      TRY

        ADOStoredProc[color= #339933;].Connection[color= #339933;]:=ADOConnection1[color= #339933;];

        ADOStoredProc[color= #339933;].Prepared[color= #339933;]:= [color= #000000; font-weight: bold;]true[color= #339933;];

        ADOStoredProc[color= #339933;].Parameters[color= #339933;].ParamByName[color= #009900;]([color= #0000ff;]'(CustName'[color= #009900;])[color= #339933;].value[color= #339933;]:= Edit1[color= #339933;].Text[color= #339933;];

        ADOStoredProc[color= #339933;].Parameters[color= #339933;].ParamByName[color= #009900;]([color= #0000ff;]'HireDate'[color= #009900;])[color= #339933;].Value[color= #339933;]:=DateTimePicker1[color= #339933;].[color= #990000;]Date[color= #339933;];

        ADOStoredProc[color= #339933;].Parameters[color= #339933;].ParamByName[color= #009900;]([color= #0000ff;]'Salary'[color= #009900;])[color= #339933;].Value[color= #339933;]:=Edit2[color= #339933;].Text[color= #339933;];

        ADOStoredProc[color= #339933;].Parameters[color= #339933;].ParamByName[color= #009900;]([color= #0000ff;]'Notes'[color= #009900;])[color= #339933;].Value[color= #339933;]:=Memo1[color= #339933;].Text[color= #339933;];

        ADOStoredProc[color= #339933;].ExecProc[color= #339933;];

        TRY

           ADOStoredProc[color= #339933;].Execute[color= #339933;];     [color= #666666; font-style: italic;]// ...

        EXCEPT ON E[color= #339933;]:exception [color= #b1b100;]DO

           [color= #666666; font-style: italic;]// LOG('');

        [color= #990000;]END[color= #339933;];

      FINALLY

      ADOStoredProc[color= #339933;].Free[color= #339933;];

      [color= #990000;]END[color= #339933;];

[color= #990000;]END[color= #339933;];

تم تعديل هذه المشاركة بواسطة زكيري عبد العالي في 25 فبراير 2009 في 00:29

#6

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

جزاكم الله خيراً على هذا التفصيل, و إن شاء الله نصل إلى الحل في تسريع الإتصال.

طبعاً ما ذكرتموه يبدو لي الطريقة المثلى في التعامل مع الجداول التي تنمو بسرعة و خاصة موضوع الـ transaction, و موضوع الـ Storede Proc,

و كل ما فصلتموه واضح جداً بالنسبة لجانب الدلفي,

نأتي الآن إلى الـ insert into.

الجداول التي أريد التعامل معها بواسطة الـ insert into هي الأربع جداول:

qoutationmaster_new

quotationdetails_tcoded

quotationdetails_p_master

quotationdetails_p_details

و هذه صورة توضيحية للعلاقة بينها:

post-73371-1235544691_thumb.jpg

حيث أن المفتاح الرئيسي في الجدول الأب qoutationmaster_new هو qid وهو موجود بنفس الاسم على أنه مفتاح خارجي في الجداول:

quotationdetails_tcoded

quotationdetails_p_master

كما يحتوي الجدول quotationdetails_p_master مفتاح رئيسي هو id وهو موجود بنفس الاسم على أنه مفتاح خارجي في الجدول:

quotationdetails_p_details

أي أن الجميع مرتبطين ببعض عن طريق الـ relationship.

السؤال الآن:

كيف يمكنني أن استخدم insert into لتخزين سجل في الجدول الأبqoutationmaster_new , ومن ثم أخزن في أبنائه:

quotationdetails_tcoded

quotationdetails_p_master

مجموعة من السجلات مرتبطة مع الأب qoutationmaster_new عن طريق الـ qid.

و نفس الأمر أريد تخزين سجلات في الـ quotationdetails_p_details التابع للأب quotationdetails_p_master التابع لنفس السجل الرئيسي في

الجدول qoutationmaster_new.

كيف أضمن أن ما أخزنه كله تابع لنفس السجل الرئيسي في qoutationmaster_new.

لأنني عندما كنت أستخدم الـ adotable لم أكن أعاني من هذه المسكلة لأن الـ adotable عن طريق الخاصية masterrecords و masterfields يقوم هو بتولي أمر التخزين مع الربط.

أتنمى أن أكون قد صورت المشكلة بشكل واضح.

وجزاكم الله خيراً

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

قال تعالى: ((وَمَا أَصَابََكُمْ مِنْ مُصِيبَةٍ فَبمَا كَسَبَتْ أَيْدِيكُمْ وَ يَعْفُو عَنْ كَثِيرٍ)) :.:.: الشورى: الآية 30.

IN ALLAH WE TRUST

:.:.:.:.:.:.:.:.:.:.:.:.:.:

#7

كما ذكر الاخ ناجي, فإن استخدام Adotable و التعامل مع البيانات من خلاله أمر سيئ جدا في حال التعامل مع عدد سجلات كثيرة و الافضل دائما هو استخدام Query بشكل مباشر لتنفيذ المطلوب.

كما أنّ هناك طريقة أخرى لتسريع الاداء و هي باستخدام ما يسمى Stored Procedures و التي سوف يتم كتابتها و تنفيذها في ال Server فهناك العديد من المزايا لها:

اطلع على الرابط التالي:

هل من المنطق تحويلكل جمل SQL إلى StoredProcedure؟

رب اجعلني مقيم الصلاة ومن ذريتي ربنا وتقبل دعاء.

لا تنسى: "العقل مثل العضلة كلما استخدمته أكثر كلما ازدادت قوته"

#8

السلام عليكم...

سنتحدث عن عمليات الإضافة بشكل عام مستخمين أسماء الجداول المذكورة في مشاركة الأخ Like Delphi الأخيرة.

لدينا الجداول كالتالي:

* qoutationmaster_new

  • quotationdetails_p_master


  • quotationdetails_p_details


[*]quotationdetails_tcoded

أ. إذا أردنا فقط إضافة سجل إلى الجدول الرئيسي qoutationmaster_new فالأمر سهل: فقط جملة INSERT لهذا الجدول:

  1. ADOCommand1.CommandText := 'INSERT INTO qoutationmaster_new (field_list) VALUES (value_list)';
  2. ADOCommand1.Execute;
  3.  

ب. لكننا في كثير من الأحيان نقوم بإدراج سجل في الجدول الرئيسي مع إدراج سجل أو سجلات في جدول واحد أو أكثر من الجداول الفرعية. في هذه الحالة سنحتاج إلى دالة للحصول على قيمة المفتاح الجديدة من الجدول الرئيسي بعد إضافة السجل فيه لاستخدامها مع الجداول الفرعية.

لنفترض أن لدينا ADOQuery1 نستخدمه لهذا الغرض. سننشئ دالة لإنجاز هذا الأمر:

1. في القسم private من تعريف Class الـ Form (نفرض أنها Form1) نكتب التصريح التالي:

  1.  
  2. function GetMaxID(TableName, IDFieldName: string): integer;
  3.  

2. ثم في قسم الـ implementation نكتب تعريف الدالة:

  1. function TForm1.GetMaxID(TableName, IDFieldName: string): integer;
  2. begin
  3. with ADOQuery1 do begin
  4. Close;
  5. SQL.Text := 'SELECT MAX(' + IDFieldName + ') AS MaxID FROM ' + TableName;
  6. Open;
  7. Result := FieldByName('MaxID').AsInteger;
  8. Close;
  9. end;
  10. end;
  11.  

3. في إجراء الإضافة يكون لدينا متغير باسم مثلاً Newqid من النوع Integer، و سيكون الكود قريباً من التالي:

  1. try
  2. YourConnectionName.BeginTrans;
  3. // إضافة سجل في الجدول الرئيسي
  4. ADOCommand1.CommandText := 'INSERT INTO qoutationmaster_new (field_list) VALUES (value_list)';
  5. ADOCommand1.Execute;
  6. // الحصول على قيمة المفتاح الرئيسي الجديدة
  7. Newqid := GetMaxID('qoutationmaster_new', 'qid');
  8. // إضافة سجل - أو سجلات - في الجدول الفرعي مستخدمين القيمة الجديدة للمفتاح
  9. // الجملتان التاليتان يمكن تكرارهما على قدر ما تريد إضافته من سجلات في الجدول الفرعي، طبعاً مع تغيير القيم. قد تحتاج هنا لاستخدام حلقة
  10. ADOCommand1.CommandText := 'INSERT INTO quotationdetails_tcoded (field1, field2, ..., qid, ...) VALUES (value1, value2, ..., ' + IntToStr(Newqid) + ', ...)';
  11. ADOCommand1.Execute;
  12. // نهاية الحلقة - إذا تم استخدامها
  13. // حفظ البيانات بشكل دائم
  14. if YourConnectionName.InTransaction then YourConnectionName.CommitTrans;
  15. ShowMessage('OK, data saved');
  16. except
  17. // حدث خطأ: نلغي كافة العمليات السابقة
  18. if YourConnectionName.InTransaction then YourConnectionName.RollbackTrans;
  19. ShowMessage('Error, data not saved');
  20. end;
  21.  

ج. ربما نريد أيضاً إضافة سجل أو سجلات للجدول الفرعي تابعة لسجل موجود أصلاً في الجدول الرئيسي. في هذه الحالة سيكون معروفاً لدينا قيمة المفتاح الرئيسي (مثلاً البيانات معروضة في DBGrid و المستخدم قام بتحديد أحد السجلات فيها، أو مثلاً طلبنا من المستخدم إدخال الاسم و بحثنا عنه للحصول على الرقم، أو أية طريقة أخرى). نخزن قيمة هذا المفتاح في المتغير Newqid ثم نستخدم الكود السابق مع حذف الجزء الخاص بالإضافة في الجدول الرئيسي:

  1.  
  2. // ...
  3. // كود للحصول على قيمة المفتاح الرئيسي المطلوب إضافة سجلات فرعية إليه
  4. // ...
  5. try
  6. YourConnectionName.BeginTrans;
  7. // إضافة سجل - أو سجلات - في الجدول الفرعي
  8. // الجملتان التاليتان يمكن تكرارهما على قدر ما تريد إضافته من سجلات في الجدول الفرعي، ط
    بعاً مع تغيير القيم. قد تحتاج هنا لاستخدام حلقة
  9. ADOCommand1.CommandText := 'INSERT INTO quotationdetails_tcoded (field1, field2, ..., q
    id, ...) VALUES (value1, value2, ..., ' + IntToStr(Newqid) + ', ...)';
  10. ADOCommand1.Execute;
  11. // نهاية الحلقة - إذا تم استخدامها
  12. // حفظ البيانات بشكل دائم
  13. if YourConnectionName.InTransaction then YourConnectionName.CommitTrans;
  14. ShowMessage('OK, data saved');
  15. except
  16. // حدث خطأ: نلغي كافة العمليات السابقة
  17. if YourConnectionName.InTransaction then YourConnectionName.RollbackTrans;
  18. ShowMessage('Error, data not saved');
  19. end;
  20.  

ربما نتحدث في مرة قادمة عن عمليات التحديث (UPDATE) و الحذف (DELETE) في الجداول الفرعية، و كذلك الحذف من الجدول الرئيسي.

نرجو الاستفادة و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#9

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

شكراً أخي وائل على المرور.

أخي najy_zl جزاك الله خيراً على التفصيل, و لكن السؤال الآن هو:

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

أي أنني أخشى من الـ max بأن تحضر رقم سجل يتبع لمستخدم آخر دخل في نفس الوقت.

هل هذا ممكن الحصول؟ أم أنه بما أننا نفعل كل هذا ضمن الصفقة فنأمن من حصول تلك المشكلة؟

وجزاك الله خيراً.

قال تعالى: ((وَمَا أَصَابََكُمْ مِنْ مُصِيبَةٍ فَبمَا كَسَبَتْ أَيْدِيكُمْ وَ يَعْفُو عَنْ كَثِيرٍ)) :.:.: الشورى: الآية 30.

IN ALLAH WE TRUST

:.:.:.:.:.:.:.:.:.:.:.:.:.:

#10

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

إخواني الكرام جزاكم الله خيراً على كل ما قدمتوه من علم و أبشركم بأنها الطريقة نجحت و تعمل بكفاءة,

و الحمد لله البرنامج أصبح أسرع بأضعاف من ذي قبل.

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

قال تعالى: ((وَمَا أَصَابََكُمْ مِنْ مُصِيبَةٍ فَبمَا كَسَبَتْ أَيْدِيكُمْ وَ يَعْفُو عَنْ كَثِيرٍ)) :.:.: الشورى: الآية 30.

IN ALLAH WE TRUST

:.:.:.:.:.:.:.:.:.:.:.:.:.:

#11

قال الاخ like delphi بان الطريقة نجحت وان البرنامج يعمل باسرع من ذي قبل وانا اقول ان الطريقة لم تنجح وان البرنامج مازال بطئ وابطأ من قبل فقط لاني وجدت في هذه الصفحة ما لم اجده في كل المنتدى وتمنيت الا تنتهي فانا لم استعمل sql server من قبل حيث كنت اعمل على الانتربيز ولكني احببت استعماله وكانت هذه الصفحة هي المرشد لي وقد قام الاخوة فيها بالتوسع بالشرح وهي طريقة كثيرا مانفتقدها حيث نجد في بعض الردود من الالغاز اكثر من السؤال نفسه ولكن قام الاخوة في هذا الموضوع بالتوسع في الشرح والامثلة التى كانت فعلا بمثابة درس متكامل في الموضوع ولدي هنا سؤال من واقع تجاربكم الشخصية لو كنت تقوم بعمل برنامج مبيعات ومخازن لشركة ما فهل تعمله باستخدام sql server ام الانتربيز وانا هنا لااريد مقالة حول مزايا هذه وعيوب هذه لان الحكم بهذه الطريقة صعب فلديا ملفات pdf طويلة عن الموضوع ولكن اريد تحديد السكيوال ام الانتربيز فقط فانا اعرف ان لمشرفينا الافاضل من الخبرة التى تجعلني اطمئن لارائهم

وايضا عند استخدام هذا الكود للاستعلام عن حقل تاريخ

q1.SQL.Add('select * from "table1" where "field1" between'+''''+datetostr(datetimepicker1.Date)+''''+'and'+
''''+datetostr(datetimepicker2.Date)+'''');

ومع انه في الجدول التارخ مكتوب كالتالي اليوم ثم الشهر ثم السنة وفي datetimepicker مكتوب بنفس الطريقة الا ان الاستعلام بتنفذ كما لوكان التاريخ مكتوب على الطريقة الامريكية لكتابة التاريخ اي الشهر ثم اليوم ثم السنة ولقد تعبت حتى انتبهت لذلك فكنت اقوم بتشغيل الكويرى ولا يعطى نتائج وبما اني جديد في استعمال السكيوال سيرفر اعتقدت باني لا اعرف الطريقة الصحيحة للاستعلام عن التاريخ ثم بالصدفة وعند تغيير التاريخ في datetimepicker وجدته يعمل ولكن يعتبر الشهر هو اليوم والعكس وطبعا الاعدادات الاقليمية عندي هي بالترتيب التالي اليوم الشهر السنة وكما قلت المعروض في الجدول هو اليوم الشهر السنة والمعروض في مكون التاريخ على الفورم هو اليوم الشهر السنة ولكن الاستعلام يتنقذ بالشهر اليوم السنة فهل هناك خاصية او اي شئ اخر يمكن تعديله للتغلب على المشكلة لان تغيير الاعدادات الى نظام الشهر اليوم السنة لن تكون مقبولة من المستخدمين

تم تعديل هذه المشاركة بواسطة moa mohsen في 2 مارس 2009 في 02:44

#12

أولا اشكروا الاخ ناجي فماشاء الله عنه دائما يستفيض في الشرح, بارك الله في علمه.

اقتباس
ولكن اريد تحديد السكيوال ام الانتربيز

لم اتعامل من قبل مع الانتربرايز, وإنما تعاملت مع SQL Server, و أعتقد أنه ممتاز و مناسب للعمل على المشاريع التي تحتاجها.

اقتباس
ومع انه في الجدول التارخ مكتوب كالتالي اليوم ثم الشهر ثم السنة وفي datetimepicker مكتوب بنفس الطريقة الا ان الاستعلام بتنفذ كما لوكان التاريخ مكتوب على الطريقة الامريكية لكتابة التاريخ اي الشهر ثم اليوم ثم السنة ولقد تعبت حتى انتبهت لذلك فكنت اقوم بتشغيل الكويرى ولا يعطى نتائج وبما اني جديد في استعمال السكيوال سيرفر اعتقدت باني لا اعرف الطريقة الصحيحة للاستعلام عن التاريخ ثم بالصدفة وعند تغيير التاريخ في datetimepicker وجدته يعمل ولكن يعتبر الشهر هو اليوم والعكس وطبعا الاعدادات الاقليمية عندي هي بالترتيب التالي اليوم الشهر السنة وكما قلت المعروض في الجدول هو اليوم الشهر السنة والمعروض في مكون التاريخ على الفورم هو اليوم الشهر السنة ولكن الاستعلام يتنقذ بالشهر اليوم السنة فهل هناك خاصية او اي شئ اخر يمكن تعديله للتغلب على المشكلة لان تغيير الاعدادات الى نظام الشهر اليوم السنة لن تكون مقبولة من المستخدمين

يمكنك استخدام SET DATEFORMAT الخاصة بالاسكيول سيرفر وذلك بعد إنشاء الاتصال مع قاعدة البيانات لتحديد الفورمات التي سوف تستخدم في جميع الاجراءات التي سوف تنفذ عبر هذا الاتصال.

  1. SET DATEFORMAT dmy
  2.  

أو يمكن استخدام كلا التابعين Convert أو Cast لتغيير الفورمات في Query string مباشرة, ابحث في Books Online عنهما أوفي منتدى SQL Server أو في الانترنت.

رب اجعلني مقيم الصلاة ومن ذريتي ربنا وتقبل دعاء.

لا تنسى: "العقل مثل العضلة كلما استخدمته أكثر كلما ازدادت قوته"

#13

شكرا اخي على ردك السريع وشكرا للاخ ناجي على توضيحاته وبالنسبة للسؤال عن اي قاعدة بيانات استخدم ومع انني لا احب الحكم على الاشياء التى لا املك الخبرة الكافية التى تؤهلني للحكم عليها الا انني وفي الفترة البسيطة التى اطلعت ركزت فيها على sql server وكما قلت في الفترة الاخيرة فقط الا اني اكتشفت فيه عدة نواحي تجعله افضل بكثير من الانتربيز و كونك اخي وائل تستعمله فهذا يضاف الى رصيد sql

كما اني اطلعت على برنامج اعتقد ان الاخ ناجي هو مبرمجه يستخدم السكيوال يعمل على شبكة

وبالنسبة للسؤال الثاني فساقوم بتجربة ماتفضلت به وشكرا

#14

السلام عليكم...

أنا أيضاً أكاد لا أفقه شيئاً في الـ InterBase لكن - في حدود معرفتي المتواضعة بالـ SQL Server - فإنني معجب به (أي SQL Server) لأنه على الأقل يوفر لي كل ما أحتاجه و بكفاءة.

اقتباس
ولقد تعبت حتى انتبهت لذلك

نعم، و هذا ما حدث معي في بدايات تعلمي للبرمجة (البداية كانت مع VB) ما دفعني لإجراء العديد من التجارب و تغييرات تنسيق التاريخ في لوحة التحكم... إلخ.

و النتيجة التي وصلت إليها كانت بسيطة و حلت مشكلتي مع حقول التاريخ!

جميع محركات قواعد البيانات تتعامل داخلياً مع التاريخ بالصيغة الأمريكية (الشهر ثم اليوم ثم السنة). أما العرض على الشاشة فيعتمد على الإعدادات الإقليمية للتاريخ في لوحة التحكم التابعة لـ Windows.

طبعاً يمكننا تغيير طريقة عرض التاريخ عند تصميمنا لأي برنامج (مثلاً وجود خاصية Format أو DateFormat أو أياً كان اسمها) لكن ذلك يبقى متعلقاً بالعرض فقط.

بصرف النظر عن إعدادات التاريخ في لوحة التحكم فإننا عندما نرسل قيمة تاريخ (ضمن جملة SQL) إلى محرك قواعد البيانات (SQL Server أو Access أو غيرهما) فيجب أن تكون بالتنسيق الأمريكي (في دلفي نستعمل الدالة FormatDateTime) كما يلي مثلاً:

  1. q1.SQL.Text := 'SELECT * FROM table1 WHERE (field1 BETWEEN ' + QuotedStr(FormatDateTime('mm/dd/
    yyyy', datetimepicker1.Date)) + ') AND (' +
  2. QuotedStr(FormatDateTime('mm/dd/yyyy', datetimepicker2.Date)) + ')';
  3.  
  4.  

إن الدالة FormatDateTime تأخذ بالاعتبار تنسيق التاريخ في الحاسوب.

و دائماً كانت النتائج صحيحة.

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

نرجو الاستفادة و السلام.

تم تعديل هذه المشاركة بواسطة najy_zl في 2 مارس 2009 في 23:32

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#15

شكرا للاخوين ولن اذكر الاسماء حتى لا اذكر احدهما قبل الاخر

بالنسبة لـ SET DATEFORMAT dmy قمت بوضعها في كويري في برنامج السكيوال وبعد الكمبايل لم يظهر خطأ ولكن لم يحدث تغيير

وفي مكونات ado لم اجد اداة من ادوات ادو تعمل معها ولم اتوصل الى مكانها الصحيح والم اشأ ان اسأل عن ذلك قبل ان ايأس من معرفة مكانها ثم جاء رد الاخ ناجي وباستعماله حللت المشكلة عفوا لم تحل المشكلة لكي تبقى هذه الصفحة نشطة فمازال لدي الكثير اسأل عنه في sql server

اكرر شكري للاخوين

#16
اقتباس
بالنسبة لـ SET DATEFORMAT dmy قمت بوضعها في كويري في برنامج السكيوال وبعد الكمبايل لم يظهر خطأ ولكن لم يحدث تغيير

لن يحدث شيئ يظهر, ولكن صيغة التاريخ من الان وصاعدا تغييرت في جميع ال Queries التي سوف تقوم بتنفيذها عبر هذا ال Connection.

حاول تنفيذ تعليمات SQL في Query Analyzer على حقول تاريخ بعد تنفيذ التعليمة ولاحظ صيغ التاريخ.

رب اجعلني مقيم الصلاة ومن ذريتي ربنا وتقبل دعاء.

لا تنسى: "العقل مثل العضلة كلما استخدمته أكثر كلما ازدادت قوته"

#17

شكرا اخي وائل على اهتمامك بالفعل بعد تنفيذ العملية اصبح الكويري المكتوب في برنامج السكيوال يعمل على اساس اليوم الشهر السنة

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