۱۳۸۸/۰۸/۲۸

يافتن تداخلات Collations در SQL Server


اگر ديتابيس خود را در طي چند سال از يك نگارش به نگارشي ديگر يا از يك سرور به سروري ديگر منتقل كرده باشيد، به احتمال زياد به مشكلات Collations هم برخورده‌ايد. يكي از فيلدها Arabic_CI_AS است (بجا مانده از دوران قبل از SQL Server 2008) در يك جدول و در جدولي ديگر فيلدي تازه‌اي با Collation از نوع Persian_100_CI_AS تعريف شده است. Collations نحوه ذخيره سازي و مقايسه رشته‌ها را كنترل مي‌كنند. زمانيكه يك جدول جديد را در SQL Server ايجاد مي‌كنيم، اگر Collation فيلدها به صورت صريح ذكر نگردند، بر مبناي همان Collation پيش فرض ديتابيس تعريف خواهند شد.
بنابراين اگر پس از استفاده از SQL Server 2008 و تنظيم Collation پيش فرض ديتابيس به Persian_100_CI_AS ، به اين موارد دقت نكنيم، دير يا زود دچار مشكل خواهيم شد.
عمليات مرتب سازي با وجود تداخلات Collations مشكل ساز نمي‌شود (خطايي دريافت نمي‌كنيد)، اما ممكن است الزاما صحيح عمل نكند. مشكل از آنجايي آغاز مي‌شود كه قصد داشته باشيم داده‌ها را مقايسه كنيم يا join ايي بين اين دو جدول با فيلدهاي ناهمگون از لحاظ Collation ايجاد نمائيم. در اين حالت حتما خطاهاي تداخل Collation را دريافت كرده و كوئري‌هاي ما اجرا نخواهند شد.
Cannot resolve collation conflict for equal to operation

يك راه حل اين است كه در حين join به صورت صريح collation هر دو فيلد ذكر شده را به صورت يكسان ذكر كنيم كه بيشتر يك مرهم موقتي است تا راه حل اصولي. براي مثال:
SELECT ID
FROM ItemsTable
INNER JOIN AccountsTable
WHERE ItemsTable.Collation1Col COLLATE DATABASE_DEFAULT
= AccountsTable.Collation2Col COLLATE DATABASE_DEFAULT
راه ديگر اين است كه مشخص كنيم كه Collation كدام فيلدها در ديتابيس با Collation پيش فرض ديتابيس تطابق ندارند. سپس بر اساس اين ليست شروع به تغيير Collations نمائيم.
اسكريپت زير تمام فيلدهاي ناهمگون از لحاظ Collation ديتابيس جاري را براي شما ليست خواهد كرد:
DECLARE @defaultCollation NVARCHAR(1000)
SET @defaultCollation = CAST(
DATABASEPROPERTYEX(DB_NAME(), 'Collation') AS NVARCHAR(1000)
)

SELECT C.Table_Name,
Column_Name,
Collation_Name,
@defaultCollation DefaultCollation
FROM Information_Schema.Columns C
INNER JOIN Information_Schema.Tables T
ON C.Table_Name = T.Table_Name
WHERE T.Table_Type = 'Base Table'
AND RTRIM(LTRIM(Collation_Name)) <> RTRIM(LTRIM(@defaultCollation))
AND COLUMNPROPERTY(OBJECT_ID(C.Table_Name), Column_Name, 'IsComputed') = 0
ORDER BY
C.Table_Name,
C.Column_Name
براي مثال جهت تغيير Collation فيلد Serial از جدول tblArchive از نوع nvarchar با طول 200 به Persian_100_CI_AS مي‌توان از دستور T-SQL زير استفاده كرد:
ALTER TABLE [tblArchive] ALTER COLUMN [Serial] nvarchar(200) COLLATE Persian_100_CI_AS not null