2009/01/13

مشكل ي و ك فارسي و عربي در يك ديتابيس اس كيوال سرور


ديروز به من اطلاع دادند كه در يكي از برنامه‌ها دو تا گروه "تاسيسات مكانيكي" پيدا شده!!
تاسيسات مكانيكي
تاسيسات مکانیکی

استاندارد اين شركت، استفاده از kbdfa.dll مخصوص و نسبتا قديمي است. بنابراين استاندارد مورد استفاده همان ي و ك عربي است. (كاري ندارم خوب است يا بد، يا بايد اينطور باشد يا نه، بحث اين است كه فعلا اينطور است و قرار نيست چيزي عوض بشود!)
در مثال فوق، ي و ك عبارت دوم فارسي است. يعني نصب kbdfa.dll روي ويندوز تازه نصب شده، فراموش شده بوده.

راه حل‌ها:
الف) قبل از ثبت، يكسان سازي صورت گيرد. يعني اجراي متدي شبيه به متد زير بر روي هر ورودي متني فارسي:

public string SafeFarsiStr(string input)
{
return input.Replace("ی", "ي").Replace("ک", "ك");
}

ب) خوب، الان كه اين يكسان سازي صورت نگرفته چه بايد كرد؟
اسكريپتي را تهيه كرده‌ام (مخصوص SQL Server 2005 به بعد) به صورت زير كه اين تبديل را براي شما انجام مي‌دهد.
به صورت خودكار تمامي فيلدهاي متني كليه جداول ديتابيس جاري شما را يافته و ي و ك آن‌ها را يكسان مي‌كند. البته همانطور كه عرض شد، مطابق استاندارد اين شركت و استفاده از فايل kbdfa.dll قديمي مورد استفاده، تمام ي و ك هاي فارسي به عربي تبديل مي‌شوند.

--اسكريپتي براي يك دست سازي ي و ك در تمامي ركوردهاي تمامي جداول ديتابيس جاري
-- اسكريپت زير ي و ك فارسي را به عربي تبديل مي‌كند
-- در صورت نياز به حالت عكس ، جاي مقادير عددي يونيكد را تعويض نمائيد

USE TestDb;

DECLARE @Table NVARCHAR(MAX),
@Col NVARCHAR(MAX)

DECLARE Table_Cursor CURSOR
FOR
--پيدا كردن تمام فيلدهاي متني تمام جداول ديتابيس جاري
SELECT a.name, --table
b.name --col
FROM sysobjects a,
syscolumns b
WHERE a.id = b.id
AND a.xtype = 'u' --User table
AND (
b.xtype = 99 --ntext
OR b.xtype = 35 -- text
OR b.xtype = 231 --nvarchar
OR b.xtype = 167 --varchar
OR b.xtype = 175 --char
OR b.xtype = 239 --nchar
)

OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Table,@Col
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC (
'update [' + @Table + '] set [' + @Col +
']= REPLACE(REPLACE(CAST([' + @Col +
'] as nvarchar(max)) , NCHAR(1740), NCHAR(1610)),NCHAR(1705),NCHAR(1603)) '
)

FETCH NEXT FROM Table_Cursor INTO @Table,@Col
END CLOSE Table_Cursor DEALLOCATE Table_Cursor

توضيحات و نكاتي در مورد اسكريپت فوق:
الف) براي آشنايي با انواع XType Datatype مورد استفاده در كوئري فوق به اين آدرس مراجعه نمائيد.
ب) همانطور كه در مطالب قبلي اين وبلاگ نيز ذكر شد، امكان استفاده از تابع replace بر روي فيلدهاي text و ntext وجود ندارد. هيچ اشكالي ندارد! تمام آن‌ها به nvarchar از نوع max دار cast شده و اين مشكل به اين صورت حل مي‌شود.
ج) اس كيوال سرور اجازه تعريف يك جدول يا فيلد را به صورت متغير بكار رفته در يك كوئري T-SQL نمي‌دهد. براي حل اين موضوع بايد عبارت SQL مورد نظر را به صورت يك رشته درآورد و سپس exec كرد.
د) مجبور شدم از معاد‌ل‌هاي عددي براي دقت بيشتر كار استفاده كنم



(در كل از تابع UNICODE اس كيوال سرور براي بدست آوردن اين اعداد مي‌توان استفاده كرد)

تذكر: اين اسكريپت بر روي يك ديتابيس كاري تست شده و نتيجه رضايت بخش بوده؛ اما اگر شما روزي خواستيد از آن استفاده كنيد، حتما full backup را قبل از اجراي آن فراموش نكنيد و پس از اجرا، تابع SafeFarsiStr فوق را براي ادامه كار حتما لحاظ نمائيد يا از يك kbdfa.dll هماهنگ استفاده كنيد.


9 نظرات:

  1. بعضی نسخه‌های kbdfa.dll بر اساس استاندارد ۲۹۰۱ هستند. استاندارد ۲۹۰۱ توسط استاندارد جدیدتر ۹۱۴۷ باطل اعلام گردید. البته اختلاف این دو تا استاندارد خیلی توی چشم نمی‌زند.
    یک درایور خوب هم برای استاندارد ۹۱۴۷ وجود دارد که من در ویندوز ویستا و چندین ویندوز ایکس پی از آن استفاده می‌کنم بدون این که مشکلی برایم به وجود آمده باشد.

    آدرس درایور: http://prdownloads.sourceforge.net/farsitools/persiankeyboard.zip?download

    مطالعه استاندارد: http://fa.farsiweb.ir/mediawiki-fa/images/a/a9/Isiri-9147.pdf

    لینک استاندارد در سایت سازمان استاندارد: http://www.isiri.org/asp/account/checklog.asp?ID=9147.pdf


    بیاید از زبان فارسی حمایت کنیم.

    پاسخحذف
  2. مطلب خوبی بود اما من هم با افشار موافقم که باید از فارسی حمایت کرد و بهتره که بجای جایگزینی حروف فارسی با عربی ، بهتره که از فونتهای استاندارد استفاده بشه و حروف عربی به فارسی تبدیل بشن
    -
    از مطلب خوبتون هم واقعا ممنونم

    پاسخحذف
  3. اگر اين مورد به نحوي در خود برنامه‌هاي پايگاه داده حل بشود، اين مسايل ديگر وجود نخواهد داشت.
    مثلا اس كيوال سرور ي و ى را به يك صورت پردازش كند و جدا از هم درنظر نگيرد.
    اين مورد الان در جستجوي گوگل هم مشهود است و مشكل زا. شما با ي يك سري جواب مي‌گيريد و با ى يك سري ديگر. اين مورد مهم است!

    پاسخحذف
  4. تو برنامه تون (دات نتی) با استفاده از MessageFilter می تونید کلیه ورودی ها رو تحت کنترل داشته باشید.

    پاسخحذف
  5. مرسی از کمکت عالی بود...

    پاسخحذف
  6. با سلام و تشکر از مطالب مفیدتون.
    استاندارد سیستم من فارسی هست و میخوام همون هم ذخیره بشه.
    من این اسکریپت رو اجرا کردم (البته با تغییر در replace و جابه جا کردن فارسی و عربی) و دیتابیس رو درست کرد.
    فقط الان یه مشکلی دارم(البته فکر می کنم قبل از این اسکریپت هم بوده): هر insert که می زنم ، خود SQL "ی" فارسی رو به "ي" عربی تبدیل می کنه و ذخیره می کنه. یعنی من 1740 می فرستم ولی 1610 ذخیره میشه. این مشکل در مورد ک وجود نداره و همون 1705 فرستاده و ذخیره میشه.
    من از SQL 2005 express و Collation Arabic_CI_AS استفاده می کنم.

    پاسخحذف
  7. مشكل از SQL Server نيست. مشكل از درايور صفحه كليد شما است كه به نظر ايراد دارد. بايد ورودي كدهاي خودتون رو در تمام قسمت‌هايي كه insert دارد بررسي كنيد (قبل از ورود به ديتابيس اين تصحيح بايد انجام شود مانند استفاده از تابع SafeFarsiStr فوق).

    پاسخحذف
  8. اثبات اين مدعا هم كه مشكل از SQL Server نيست ساده است (فيلد با collation عربي ايجاد شده و ى فارسي در آن ثبت شده است و سپس گزارشگيري):
    DECLARE @tbl AS TABLE (f1 NVARCHAR(50) COLLATE Arabic_CI_AS NULL)
    INSERT INTO @tbl(f1) VALUES(NCHAR(1740))
    SELECT * FROM @tbl

    پاسخحذف