أهم أقسام المدونة

الصفحات

الاثنين، 6 فبراير 2017

UPDATE .WRITE


التحرير المتتابع للسجلات، باستخدام الأمر UPDATE .WRITE:

عند التعامل مع أعمدة تحتوي على بيانات من النوع text أو ntext أو image في سيكيويل سيرفر 2000، كانت تواجهنا مشكلة خطيرة، وهي كيفية إرسال كم ضخم من البيانات من العميل إلى الخادم لحفظه في هذه الأعمدة.. افرض أنك تريد وضع صورة حجمها 10 ميجا في خانة من النوع image بالاستعلام التالي:

UPDATE Publishers

SET Logo = @Logo

WHERE ID = 1

حيث @Logo هو معامل من النوع image يحمل بيانات الصورة.. لو حاولت تنفيذ هذا الاستعلام، فسيستغرق إرسال الصورة إلى الخادم وقتا ملموسا (تبعا لسرعة الاتصال، وحجم العبء على الخادم في تلك اللحظة)، وقد يتعطل برنامجك عن الاستجابة، وقد ينفد وقت الانتظار Timeout قبل إتمام العملية.

ويمكنك أن تجرب الاستعلام السابق بضغط الزر Add Logo في المشروع WriteLargeData.. جرب اختيار صورة حجمها كبير لترى تأثير هذا.

وللأسف، لا يفيدك استخدام استعلام كالتالي:

UPDATE Publishers

SET Logo = Logo + @Logo

WHERE ID = 1

هذا الاستعلام يحاول إضافة جزء من بيانات الصورة موضوع في المعامل @Logo إلى البيانات الموجودة فعليا في عمود الصورة Logo.. لكن للأسف، سيؤدي هذا إلى حدوث خطأ، لأن سيكويل سيرفر لا يعرف كيف يجمع بيانات من النوع image !

ولحل هذه المشكلة، قدمت سيكويل سيرفر 2005 الأنواع القصوى الجديدة varchar(max), nvarchar(max), varbinary(max) وهي مخصصة للتعامل مع الكائنات الثنائية الضخمة Binary Large Objects (BLOBs)، فهي تستطيع استقبال بيانات يصل حجمها إلى حوالي 2 جيجا بايت، كما يمكنها التعامل مع البيانات الثنائية والحروف بطريقة تتابعية Sequential، أي أنك تستطيع الكتابة فيها في أي موضع، أو القراءة منها من أي موضع.

لهذا إذا كنت تنوي التعامل مع بيانات ضخمة BLOBs، فالأفضل أن تستخدم:

- النـوع varchar(max) بـدلا من النوع Text.

- والنوع nvarchar(max)  بدلا من النوع ntext.

- والنوع varbinary(max) بدلا من النوع image.

وللكتابة التتابعية في هذه الأنواع الجديدة، قدمت T-SQL الصيغة الجديدة التالية لأمر التحديث:

UPDATE اسم_الجدول

SET اسم_العمود   .WRITE (@Value, @Offset, @Length )

Where شرط

فالدالة الداخلية Write تكتب البيانات المرسلة إلى المعامل @Value في الخانة المحددة في العمود، بدءا من الموضع @Offset، وبحيث يكون طول البيانات المكتوبة @Length.. لاحظ ما يلي:

- هذه الصيغة ستسبب خطأ إذا حاولت التعامل مع أي نوع بيانات غير الأنواع القصوى التي تنتهي بـ (MAX).

- هذه الصيغة ستسبب خطأ إذا حاولت إضافة بيانات في خانة قيمتها Null.. لهذا أمامك حلان:

1- إما أن تضيف الجزء الأول من البيانات باستخدام الصيغة العادية للأمر Update، ثم تضيف باقي أجزاء البيانات باستخدام الصيغة Update.Write .

2- وإما أن تمنع استخدام القيمة Null في خانات العمود، وفي هذه الحالة عليك استخدام قيمة افتراضية لوضعها في الخانات الفارغة، وذلك بوضع القيمة صفر في الخاصية Default Value Or Binding في خصائص العمود.. ولكي لا تؤثر هذه القيمة الافتراضية على القيمة التي ستضعها في خانات العمود، يجب أن تكون للمعامل @Offset القيمة صفر عند كتابة أول جزء من البيانات، لتوضع في بداية الخانة بدلا من أية بيانات موجودة.

- إذا أرسلت إلى المعامل @Value القيمة Null، فسيتم تجاهل المعامل @Length، وسيتم حذف القيمة الموجودة في الخانة في الموضع المحدد في المعامل @Offset.

- إذا أرسلت إلى المعامل @Offset القيمة Null، فسيتم تجاهل المعامل @Length، وستضاف قيمة المعامل @Value إلى نهاية البيانات الموجودة حاليا في الخانة.

- إذا أرسلت إلى المعامل @Offset قيمة أكبر من طول البيانات الموجودة في الخانة، فسيحدث خطأ.. ولكي تكتب بعد نهاية البيانات الموجودة، يجب أن ترسل إلى المعامل @Offset قيمة تساوي طول البيانات الموجودة في الخانة، أو ترسل إليه القيمة Null كما أوضحنا في الملحوظة السابقة.

- إذا أرسلت إلى المعامل @Length القيمة Null، فسيتم حذف جميع البيانات التالية للموضع المحدد في المعامل @Offset.

وستجد مثالا لاستخدام هذه الصيغة لكتابة بيانات صورة أول ناشر في العمود Logo2 في الجدول Publishers بطريقة تتابعية، وذلك في الزر Update .Write في المشروع WriteLargeData، وفيه نستخدم الاستعلام:

UPDATE Publishers

SET Logo2  .WRITE (@Logo, @Offset , @Length)

WHERE ID = 1

لاحظ أن العمود Logo2 من النوع varbinary(MAX)، وأن قيمته الافتراضية هي 0.. ولكتابة بيانات الصورة في الخانة الأولى من هذا العمود تتابعيا، سنرسل أول 100 وحدة ثنائية Byte لحفظها في الخانة بدءا من الموضع رقم صفر، ثم نرسل 100 وحدة تالية لحفظها في الخانة بدءا من الموضع رقم 100، ونستمر في فعل هذا إلى أن ننتهي من كتابة بيانات الصورة.. هذا معناه أننا سنستخدم الاستعلام Update .Write عدة مرات (وذلك من خلال حلقة تكرار Loop)، لكن مع تغيير المعاملات المرسلة إلى الدالة الداخلية .Write في كل مرة.. طبعا إرسال 100 وحدة ثنائية في كل مرة، أفضل بكثير من إرسال 10 ميجا أو أكثر دفعة واحدة.. لكن هذا قد يصير عبئا خطيرا على البرنامج إذا كانت الصورة ضخمة جدا، بسبب زيادة عدد مرات إرسال البيانات من العميل إلى الخادم.. لهذا عليك اختيار حجم مناسب لأجزاء البيانات التي ترسلها، بحيث لا يكون كبيرا جدا فيدمر الذاكرة ويبطئ عملية الإرسال، ولا يكون صغيرا جدا فيؤدي إلى إضاعة وقت كبير من الخادم بسبب كثرة عدد الأوامر المرسلة إليه من عميل واحد.. ربما يكون الأنسب مثلا أن تستخدم 1024 وحدة ثنائية (1 كيلو بايت) لكل جزء.. لكن هذا سيجعلك ترسل الصورة التي حجمها 1 ميجا فقط على حوالي 1000 مرة.. ما زال هذا يبدو كثيرا.. أليس كذلك؟.. لو شئت رأيي، فإن إرسال 100 كيلو في كل مرة سيكون مناسبا، فهذا سيرسل الصورة التي حجمها 1 ميجا باستخدام 10 أوامر فقط وهذا ليس كثيرا، كما أن 100 كيلو ليس بالحجم المقلق الذي يستغرق وقتا ملموسا عند إرساله إلى الخادم.. لكن عليك في هذه الحالة أن تجعل للخاصية Timeout الخاصة بالاتصال الذي تستخدمه قيمة أكبر قليلا، ولتكن 120 ثانية على سبيل الاحتياط.. وسنعرف كيف نفعل هذا لاحقا عند دراسة كائن الاتصال وكائن الأمر.

 

ملحوظة:
الإصدارات القديمة من T-SQL، كانت تستخدم الأوامر UPDATETEXT و READTEXT و WRITETEXT للتعامل مع البيانات الضخمة الموجودة في الأعمدة من النوع image أو text أو ntext.. لكن لا ينصح باستخدام هذه الأوامر الآن لأنها ستزال من لغة الاستعلام، وبدلا من هذا عليك استخدام الأنواع القصوى، والصيغة Update .Write.

 

من كتاب: من الصفر إلى الاحتراف برمجة قواعد البيانات في فيجوال بيزيك دوت نت ADO .NET.. للتنزيل:


 

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.