Skip to content

فصل ۷ — بازآرایی‌های کیفیت داده (Data Quality Refactorings)

بازآرایی‌های کیفیت داده (Data Quality Refactorings) تغییراتی هستند که کیفیت اطلاعات موجود در پایگاه داده را بهبود می‌دهند. این بازآرایی‌ها سازگاری و نحوهٔ استفاده از مقادیر ذخیره‌شده در پایگاه داده را بهبود می‌بخشند و/یا تضمین می‌کنند. بازآرایی‌های کیفیت داده عبارت‌اند از:

  • Add Lookup Table
  • Apply Standard Codes
  • Apply Standard Type
  • Consolidate Key Strategy
  • Drop Column Constraint
  • Drop Default Value
  • Drop Non-Nullable Constraint
  • Introduce Column Constraint
  • Introduce Common Format
  • Introduce Default Value
  • Make Column Non-Nullable
  • Move Data
  • Replace Type Code With Property Flags

مسائل رایج هنگام پیاده‌سازی بازآرایی‌های کیفیت داده

از آن‌جا که بازآرایی‌های کیفیت داده مقادیر دادهٔ ذخیره‌شده در پایگاه داده را تغییر می‌دهند، چند مسئله برای همهٔ آن‌ها مشترک است. در نتیجه باید موارد زیر را انجام دهید:

  1. رفع محدودیت‌های شکسته‌شده. ممکن است محدودیت‌هایی روی دادهٔ تحت‌تأثیر تعریف کرده باشید. در این صورت می‌توانید ابتدا Drop Column Constraint (صفحهٔ ۱۷۲) را اعمال کنید تا محدودیت را حذف کنید، سپس Introduce Column Constraint (صفحهٔ ۱۸۰) را اعمال کنید تا محدودیت را با بازتاب مقادیر دادهٔ بهبودیافته دوباره اضافه کنید.

  2. رفع نماهای شکسته‌شده. نماها (view) اغلب مقادیر دادهٔ هاردکدشده را در بند WHERE خود ارجاع می‌دهند، معمولاً برای انتخاب زیرمجموعه‌ای از داده. در نتیجه، وقتی مقادیر داده تغییر می‌کنند، این نماها ممکن است شکسته شوند. باید این نماهای شکسته را با اجرای مجموعهٔ تست و جستجوی تعاریف نمایی که ستون‌هایی را که دادهٔ تغییریافته در آن‌ها ذخیره شده ارجاع می‌دهند، پیدا کنید.

  3. رفع stored procedureهای شکسته‌شده. متغیرهای تعریف‌شده در یک stored procedure، هر پارامتری که به آن پاس داده می‌شود، مقدار(های) بازگشتی محاسبه‌شده توسط آن، و هر SQL تعریف‌شده در آن، بالقوه به مقادیر دادهٔ بهبودیافته وابسته‌اند. امیدوارانه تست‌های موجود مشکلات منطق کسب‌وکار ناشی از اعمال هر بازآرایی کیفیت داده را آشکار می‌کنند؛ در غیر این صورت باید کد stored procedureهایی را که به ستون(های) حاوی دادهٔ تغییریافته دسترسی دارند جستجو کنید.

  4. به‌روزرسانی داده. احتمالاً می‌خواهید ردیف‌های دادهٔ منبع را در طول به‌روزرسانی قفل کنید، که بر عملکرد و در دسترس بودن داده برای برنامه(ها) تأثیر می‌گذارد. دو راهبرد برای این کار دارید. اول، می‌توانید همهٔ داده را قفل کنید و سپس به‌روزرسانی‌ها را در همان زمان انجام دهید. دوم، می‌توانید زیرمجموعه‌هایی از داده — شاید حتی فقط یک ردیف در هر بار — را قفل کنید و به‌روزرسانی را فقط روی زیرمجموعه انجام دهید. رویکرد اول سازگاری را تضمین می‌کند اما خطر افت عملکرد با حجم زیاد داده را دارد — به‌روزرسانی میلیون‌ها ردیف زمان‌بر است و برنامه‌ها را از انجام به‌روزرسانی در این مدت بازمی‌دارد. رویکرد دوم به برنامه‌ها اجازه می‌دهد در طول فرایند به‌روزرسانی با دادهٔ منبع کار کنند، اما خطر ناسازگاری بین ردیف‌ها را دارد چون برخی مقادیر قدیمی و «کم‌کیفیت» و برخی به‌روز شده‌اند.

Add Lookup Table — افزودن جدول مرجع

برای یک ستون موجود، یک جدول مرجع (lookup table) ایجاد کنید.

انگیزه (Motivation)

چند دلیل برای اعمال Add Lookup Table وجود دارد:

  • معرفی یکپارچگی ارجاعی. ممکن است بخواهید یک محدودیت یکپارچگی ارجاعی روی Address.State موجود معرفی کنید تا کیفیت داده تضمین شود.
  • فراهم کردن جستجوی کد. بسیاری از اوقات می‌خواهید فهرست تعریف‌شده‌ای از کدها در پایگاه داده ارائه دهید به‌جای داشتن enumeration در هر برنامه. جدول مرجع اغلب در حافظه cache می‌شود.
  • جایگزینی محدودیت ستون. وقتی ستون را معرفی کردید، محدودیت ستون اضافه کردید تا تعداد کمی از مقادیر کد صحیح باقی بماند. اما با تکامل برنامه(ها)، به مقادیر کد بیشتری نیاز پیدا کردید تا به نقطه‌ای رسیدید که نگهداری مقادیر در جدول مرجع به‌جای به‌روزرسانی محدودیت ستون آسان‌تر بود.
  • ارائهٔ توصیف‌های تفصیلی. علاوه بر تعریف کدهای مجاز، ممکن است بخواهید اطلاعات توصیفی دربارهٔ کدها را هم ذخیره کنید. مثلاً در جدول State می‌توانید کد CA را به California مرتبط کنید.

معاوضه‌های بالقوه (Potential Tradeoffs)

دو مسئله هنگام افزودن جدول مرجع باید در نظر گرفته شود. اول، پر کردن داده — باید بتوانید دادهٔ معتبر برای پر کردن جدول مرجع فراهم کنید. اگرچه در تئوری ساده به نظر می‌رسد، پیامد آن این است که باید دربارهٔ معناشناسی مقادیر دادهٔ موجود در Address.State شکل ۷.۱ توافق داشته باشید. این کار آسان‌تر گفته می‌شود تا انجام. مثلاً برای معرفی جدول مرجع State، برخی برنامه‌ها با هر ۵۰ ایالت آمریکا کار می‌کنند، در حالی که برخی چهار قلمرو (Puerto Rico، Guam، District of Columbia و U.S. Virgin Islands) را هم شامل می‌شوند. در این موقعیت، یا باید دو جدول مرجع اضافه کنید — یکی برای ۵۰ ایالت و دیگری برای قلمروها — یا یک جدول واحد پیاده‌سازی کنید و منطق اعتبارسنجی مناسب را در برنامه‌هایی که فقط به زیرمجموعه‌ای از دادهٔ مرجع نیاز دارند قرار دهید.

شکل ۷.۱. افزودن جدول مرجع State.

مسئلهٔ دوم این است که افزودن محدودیت کلید خارجی (foreign key constraint) تأثیر عملکردی خواهد داشت. (برای جزئیات بازآرایی Add Foreign Key Constraint را در صفحهٔ ۲۰۴ ببینید.)

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

همان‌طور که در شکل ۷.۱ نشان داده شده، برای به‌روزرسانی طرحوارهٔ پایگاه داده باید موارد زیر را انجام دهید:

  1. ساختار جدول را تعیین کنید. باید ستون(های) جدول مرجع (State) را شناسایی کنید.
  2. جدول را معرفی کنید. State را در پایگاه داده با دستور CREATE TABLE ایجاد کنید.
  3. دادهٔ مرجع را تعیین کنید. باید مشخص کنید چه ردیف‌هایی در State درج می‌شوند. اعمال بازآرایی Insert Data (صفحهٔ ۲۹۶) را در نظر بگیرید.
  4. محدودیت ارجاعی را معرفی کنید. برای اعمال محدودیت‌های یکپارچگی ارجاعی از ستون کد در جدول(های) منبع به State، باید بازآرایی Add Foreign Key را اعمال کنید.

کد زیر DDL مربوط به معرفی جدول State و افزودن محدودیت کلید خارجی بین آن و Address را نشان می‌دهد:

sql
-- Create the lookup table.
CREATE TABLE State (
   State CHAR(2) NOT NULL,
   Name CHAR(50),
   CONSTRAINT PKState
     PRIMARY KEY (State)
);

-- Introduce Foreign Key to the lookup table
ALTER TABLE Address ADD CONSTRAINT FK_Address_State
  FOREIGN KEY (State) REFERENCES State;

مکانیک مهاجرت داده (Data-Migration Mechanics)

باید اطمینان حاصل کنید که مقادیر داده در Address.State مقادیر متناظر در State دارند. ساده‌ترین راه پر کردن State.State کپی کردن مقادیر یکتا از Address.State است. با این رویکرد خودکار، باید ردیف‌های حاصل را بررسی کنید تا مقادیر دادهٔ نامعتبر معرفی نشده باشند — در این صورت باید هم Address و هم State را به‌طور مناسب به‌روز کنید. وقتی ستون‌های اطلاعات توصیفی مانند State.Name وجود دارند، باید مقادیر مناسب را فراهم کنید؛ این کار اغلب به‌صورت دستی از طریق اسکریپت یا ابزار مدیریت داده انجام می‌شود. راهبرد جایگزین بارگذاری مقادیر در جدول State از یک فایل خارجی است.

کد زیر DDL مربوط به پر کردن جدول State با مقادیر متمایز از ستون Address.State را نشان می‌دهد. سپس داده را پاک‌سازی می‌کنیم — در این مورد اطمینان از اینکه همهٔ آدرس‌ها از کد TX به‌جای Tx یا tx یا Texas استفاده می‌کنند. گام نهایی ارائهٔ نام ایالت‌ها متناظر با هر کد ایالت است. (در مثال، فقط برای سه ایالت مقداردهی می‌کنیم.)

sql
-- Populate data in the lookup table
INSERT INTO State (State)
  SELECT DISTINCT UPPER(State) FROM Address;

-- Update the Address.StateCode to valid values and clean data
UPDATE Address SET State = 'TX' WHERE
UPPER(State) ='TX';
...

-- Now provide state names
UPDATE State SET Name = 'Florida' WHERE State='FL';
UPDATE State SET Name = 'Illinois' WHERE State='IL';
UPDATE State SET Name = 'California' WHERE State='CA';

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی State را اضافه می‌کنید، باید اطمینان حاصل کنید که برنامه‌های بیرونی اکنون از مقادیر دادهٔ جدول مرجع استفاده می‌کنند. کد زیر نشان می‌دهد برنامه‌های بیرونی چگونه می‌توانند نام ایالت را از جدول State بگیرند؛ در گذشته باید این اطلاعات را از مجموعه‌ای هاردکدشده در داخل برنامه می‌گرفتند:

java
// After code
ResultSet rs = statement.executeQuery(
  "SELECT State, Name FROM State");

برخی برنامه‌ها ممکن است مقادیر داده را cache کنند، در حالی که برخی دیگر در صورت نیاز به State دسترسی پیدا می‌کنند — cache کردن خوب کار می‌کند چون مقادیر State به‌ندرت تغییر می‌کنند. علاوه بر این، اگر همراه با Lookup Table محدودیت کلید خارجی هم معرفی می‌کنید، برنامه‌های بیرونی باید هر استثنایی که پایگاه داده پرتاب می‌کند را مدیریت کنند. برای جزئیات بازآرایی Add Foreign Key (صفحهٔ ۲۰۴) را ببینید.

Apply Standard Codes — اعمال کدهای استاندارد

یک مجموعهٔ استاندارد از مقادیر کد را روی یک ستون اعمال کنید تا مطابق مقادیر ستون‌های مشابه ذخیره‌شده در جای دیگر پایگاه داده باشد.

انگیزه (Motivation)

ممکن است به دلایل زیر نیاز به اعمال Apply Standard Codes داشته باشید:

  • پاک‌سازی داده. وقتی معنای معناشناسی یکسان برای مقادیر کد متفاوت در پایگاه داده دارید، معمولاً بهتر است آن‌ها را استاندارد کنید تا بتوانید منطق یکسان را روی همهٔ ویژگی‌های داده اعمال کنید. مثلاً در شکل ۷.۲، وقتی مقادیر Country.Code برابر USA و Address.CountryCode برابر US است، مشکل بالقوه‌ای دارید چون دیگر نمی‌توانید دو جدول را به‌درستی join کنید. یک مقدار سازگار — یکی یا دیگری — را در سراسر پایگاه داده اعمال کنید.

شکل ۷.۲. اعمال کدهای استاندارد ایالت.

  • پشتیبانی از یکپارچگی ارجاعی. وقتی می‌خواهید Add Foreign Key Constraint (صفحهٔ ۲۰۴) را روی جداول مبتنی بر ستون کد اعمال کنید، ابتدا باید مقادیر کد را استاندارد کنید.
  • افزودن جدول مرجع. هنگام اعمال Add Lookup Table (صفحهٔ ۱۵۳)، اغلب ابتدا باید مقادیر کدی که جستجو بر اساس آن است را استاندارد کنید.
  • مطابقت با استانداردهای سازمانی. بسیاری از سازمان‌ها استانداردهای دقیق داده و مدل‌سازی داده دارند که تیم‌های توسعه باید رعایت کنند. اغلب هنگام اعمال Use Official Data Source (صفحهٔ ۲۷۱) کشف می‌کنید طرحوارهٔ دادهٔ فعلی استانداردهای سازمان را رعایت نمی‌کند و باید برای بازتاب مقادیر کد منبع دادهٔ رسمی بازآرایی شود.
  • کاهش پیچیدگی کد. وقتی برای دادهٔ معناشناسی یکسان انواع مختلفی از مقادیر دارید، کد برنامهٔ اضافی برای مدیریت مقادیر متفاوت می‌نویسید. مثلاً کد برنامهٔ موجود countryCode = 'US' || countryCode = 'USA' ... به چیزی مانند countryCode = 'USA' ساده می‌شود.

معاوضه‌های بالقوه (Potential Tradeoffs)

استانداردسازی مقادیر کد می‌تواند دشوار باشد چون اغلب در بسیاری جاها استفاده می‌شوند. مثلاً چند جدول ممکن است مقدار کد را به‌عنوان کلید خارجی به جدول دیگر استفاده کنند؛ بنابراین نه‌تنها منبع بلکه ستون‌های کلید خارجی هم باید استاندارد شوند. دوم، مقادیر کد ممکن است در یک یا چند برنامه هاردکد شده باشند و به‌روزرسانی گسترده نیاز داشته باشند. مثلاً برنامه‌هایی که به جدول Country دسترسی دارند ممکن است مقدار USA را در عبارت‌های SQL هاردکد کرده باشند، در حالی که برنامه‌هایی که از جدول Address استفاده می‌کنند US را هاردکد کرده‌اند.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای اعمال Apply Standard Codes روی طرحوارهٔ پایگاه داده باید موارد زیر را انجام دهید:

  1. مقادیر استاندارد را شناسایی کنید. باید دربارهٔ مقادیر «رسمی» کد توافق کنید. آیا مقادیر از جداول برنامهٔ موجود ارائه می‌شوند یا توسط کاربران کسب‌وکار؟ در هر صورت، مقادیر باید توسط ذی‌نفع(های) پروژه پذیرفته شوند.
  2. جداولی که کد در آن‌ها ذخیره شده را شناسایی کنید. باید جداولی را که ستون کد را شامل می‌شوند شناسایی کنید. این ممکن است تحلیل گسترده و تکرارهای زیادی لازم داشته باشد تا همهٔ جداولی که کد در آن‌ها استفاده شده را پیدا کنید. توجه کنید این بازآرایی یک ستون در هر بار اعمال می‌شود؛ ممکن است چند بار برای اطمینان از سازگاری در سراسر پایگاه داده نیاز باشد.
  3. stored procedureها را به‌روز کنید. وقتی مقادیر کد را استاندارد می‌کنید، stored procedureهایی که به ستون‌های تحت‌تأثیر دسترسی دارند ممکن است نیاز به به‌روزرسانی داشته باشند. مثلاً اگر getUSCustomerAddress بند WHERE را به‌صورت Address.CountryCode="USA" دارد، باید به Address.CountryCode="US" تغییر کند.

مکانیک مهاجرت داده (Data-Migration Mechanics)

وقتی روی یک کد خاص استاندارد می‌کنید، باید همهٔ ردیف‌هایی که کدهای غیراستاندارد دارند را به مقادیر استاندارد به‌روز کنید. اگر تعداد کمی ردیف به‌روز می‌شود، یک اسکریپت SQL ساده که جدول(های) هدف را به‌روز می‌کند کافی است. وقتی باید حجم زیادی داده به‌روز کنید، یا در مواردی که کد در جداول تراکنشی تغییر می‌کند، به‌جای آن Update Data (صفحهٔ ۳۱۰) را اعمال کنید.

کد زیر DML مربوط به به‌روزرسانی داده در جداول Address و Country برای استفاده از مقادیر کد استاندارد را نشان می‌دهد:

sql
UPDATE Address SET CountryCode = 'CA' WHERE CountryCode = 'CAN';
UPDATE Address SET CountryCode = 'US' WHERE CountryCode = 'USA';

UPDATE Country SET CountryCode = 'CA' WHERE CountryCode = 'CAN';
UPDATE Country SET CountryCode = 'US' WHERE CountryCode = 'USA';

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی Apply Standard Codes اعمال می‌شود، جنبه‌های زیر برنامه‌های بیرونی باید بررسی شوند:

  1. بندهای WHERE هاردکدشده. ممکن است نیاز باشد عبارت‌های SQL را برای داشتن مقادیر صحیح در بند WHERE به‌روز کنید. مثلاً اگر مقادیر ردیف Country.Code از 'US' به 'USA' تغییر کند، باید بند WHERE را برای استفاده از مقدار جدید تغییر دهید.
  2. کد اعتبارسنجی. به‌طور مشابه، ممکن است نیاز باشد کد منبعی که مقادیر ویژگی‌های داده را اعتبارسنجی می‌کند به‌روز شود. مثلاً کدی مانند countryCode = 'US' باید برای استفاده از مقدار کد جدید به‌روز شود.
  3. ساختارهای جستجو (lookup constructs). مقادیر کدها ممکن است در «ساختارهای جستجوی» برنامه‌نویسی مختلف مانند ثابت‌ها، enumerationها و مجموعه‌ها برای استفاده در سراسر برنامه تعریف شده باشند. تعریف این ساختارها باید برای استفاده از مقادیر کد جدید به‌روز شود.
  4. کد تست. مقادیر کد اغلب در منطق تست و/یا منطق تولید دادهٔ آزمون هاردکد می‌شوند؛ باید آن‌ها را برای استفاده از مقادیر جدید تغییر دهید.

کد زیر وضعیت قبل و بعد متد خواندن آدرس‌های آمریکا را نشان می‌دهد:

java
// Before code
stmt = DB.prepare("SELECT addressId, line1, line2, " +
"city, state, zipcode, country FROM address WHERE " +
"countrycode = ?");
stmt.setString(1,"USA");
stmt.execute();
ResultSet rs = stmt.executeQuery();

// After code
stmt = DB.prepare("SELECT addressId, line1, line2, " +
"city, state, zipcode, country FROM address WHERE " +
"countrycode = ?");
stmt.setString(1,"US");
stmt.execute();
ResultSet rs = stmt.executeQuery();

Apply Standard Type — اعمال نوع استاندارد

اطمینان حاصل کنید که نوع دادهٔ یک ستون با نوع دادهٔ ستون‌های مشابه دیگر در پایگاه داده سازگار است.

انگیزه (Motivation)

بازآرایی Apply Standard Type می‌تواند برای موارد زیر استفاده شود:

  • تضمین یکپارچگی ارجاعی. وقتی می‌خواهید Add Foreign Key (صفحهٔ ۲۰۴) را روی همهٔ جداولی که همان اطلاعات معناشناسی را ذخیره می‌کنند اعمال کنید، باید انواع دادهٔ ستون‌های فردی را استاندارد کنید. مثلاً شکل ۷.۳ نشان می‌دهد چگونه همهٔ ستون‌های شماره تلفن برای ذخیره به‌صورت عدد صحیح بازآرایی می‌شوند. مثال رایج دیگر وقتی است که Address.ZipCode به‌صورت VARCHAR و Customer.Zip به‌صورت NUMERIC ذخیره شده‌اند؛ باید روی یک نوع داده استاندارد کنید تا بتوانید محدودیت‌های یکپارچگی ارجاعی اعمال کنید.
  • افزودن جدول مرجع. هنگام اعمال Add Lookup Table (صفحهٔ ۱۵۳)، می‌خواهید نوع سازگاری برای دو ستون کد استفاده شود.
  • مطابقت با استانداردهای سازمانی. بسیاری از سازمان‌ها استانداردهای دقیق داده و مدل‌سازی دارند. اغلب هنگام اعمال Use Official Data Source (صفحهٔ ۲۷۱) کشف می‌کنید طرحوارهٔ فعلی استانداردها را رعایت نمی‌کند و باید برای بازتاب نوع منبع دادهٔ رسمی بازآرایی شود.
  • کاهش پیچیدگی کد. وقتی برای دادهٔ معناشناسی یکسان انواع دادهٔ مختلف دارید، کد برنامهٔ اضافی برای مدیریت انواع ستون مختلف لازم است. مثلاً کد اعتبارسنجی شماره تلفن برای کلاس‌های Customer، Branch و Employee می‌تواند برای استفاده از یک متد مشترک بازآرایی شود.

شکل ۷.۳. اعمال انواع دادهٔ استاندارد در جداول Customer، Branch و Employee.

معاوضه‌های بالقوه (Potential Tradeoffs)

استانداردسازی انواع داده می‌تواند دشوار باشد چون ستون‌های فردی اغلب در بسیاری جاها ارجاع داده می‌شوند. مثلاً چند کلاس برنامه ممکن است به ستون ارجاع دهند و کدی که با تغییر نوع دادهٔ ستون باید تغییر کند. دوم، هنگام تلاش برای تغییر نوع دادهٔ ستون، ممکن است موقعیت‌هایی داشته باشید که دادهٔ منبع قابل تبدیل به نوع دادهٔ مقصد نیست. مثلاً وقتی Customer.Zip را به NUMERIC تغییر می‌دهید، ممکن است نتوانید کدهای پستی بین‌المللی مانند R2D 2C3 که دادهٔ کاراکتری دارند را تبدیل کنید.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای اعمال این بازآرایی، ابتدا باید نوع دادهٔ استاندارد را شناسایی کنید. باید دربارهٔ نوع دادهٔ «رسمی» ستون‌ها توافق کنید. این نوع داده باید همهٔ داده‌های موجود را پوشش دهد و برنامه‌های دسترسی بیرونی باید بتوانند آن را مدیریت کنند. (زبان‌های قدیمی‌تر گاهی نمی‌توانند انواع دادهٔ جدید را پردازش کنند.) سپس باید جداولی را که ستون(هایی) با نوع دادهٔ نیازمند تغییر دارند شناسایی کنید. این ممکن است تحلیل گسترده و تکرارهای زیادی لازم داشته باشد. توجه کنید این بازآرایی یک ستون در هر بار اعمال می‌شود؛ بنابراین ممکن است چند بار برای سازگاری در سراسر پایگاه داده نیاز باشد.

شکل ۷.۳ نشان می‌دهد چگونه ستون‌های Branch.Phone، Branch.FaxNumber و Employee.PhoneNumber را به همان نوع دادهٔ integer تغییر می‌دهیم — ستون Customer.PhoneNumber از قبل integer است و نیازی به بازآرایی ندارد. اگرچه در واقع سه بازآرایی جداگانه است، چون سه ستون در یک جدول هستند، منطقی است این سه بازآرایی را با هم انجام دهیم.

کد زیر سه بازآرایی لازم برای تغییر Branch.Phone، Branch.FaxNumber و Employee.Phone را نشان می‌دهد. ستون جدید را با Introduce New Column (صفحهٔ ۳۰۱) اضافه می‌کنیم. چون می‌خواهیم زمانی برای مهاجرت همهٔ برنامه‌ها به ستون‌های جدید بدهیم، در فاز گذار هر دو ستون را نگه می‌داریم و داده را در آن‌ها همگام می‌کنیم:

sql
ALTER TABLE Branch ADD COLUMN PhoneNumber INT;
COMMENT ON Branch.PhoneNumber "Replaces Phone, dropdate=2007-03-27"

ALTER TABLE Branch ADD COLUMN FaxNo INT;
COMMENT ON Branch.FaxNo "Replaces FaxNumber, dropdate=2007-03-27"

ALTER TABLE Employee ADD PhoneNo INT;
COMMENT ON Employee.PhoneNo "Replaces PhoneNumber, dropdate=2007-03-27"

کد زیر نحوهٔ همگام‌سازی تغییرات در Branch.Phone، Branch.FaxNumber و Employee.Phone با ستون‌های موجود را نشان می‌دهد:

sql
CREATE OR REPLACE TRIGGER SynchronizeBranchPhoneNumbers
  BEFORE INSERT OR UPDATE
  ON Branch
  REFERENCING OLD AS OLD NEW AS NEW
  FOR EACH ROW
  DECLARE
  BEGIN
    IF :NEW.PhoneNumber IS NULL THEN
      :NEW.PhoneNumber := :NEW.Phone;
    END IF;
    IF :NEW.Phone IS NULL THEN
      :NEW.Phone := :NEW.PhoneNumber;
    END IF;
    IF :NEW.FaxNumber IS NULL THEN
      :NEW.FaxNumber := :NEW.FaxNo;
    END IF;
    IF :NEW.FaxNo IS NULL THEN
      :NEW.FaxNo := :NEW.FaxNumber;
    END IF;
 END;
/

CREATE OR REPLACE TRIGGER SynchronizeEmployeePhoneNumbers
  BEFORE INSERT OR UPDATE
  ON Employee
  REFERENCING OLD AS OLD NEW AS NEW
  FOR EACH ROW
  DECLARE
  BEGIN
    IF :NEW.PhoneNumber IS NULL THEN
       :NEW.PhoneNumber := :NEW.PhoneNo;
    END IF;
    IF :NEW.PhoneNo IS NULL THEN
       :NEW.PhoneNo := :NEW.PhoneNumber;
    END IF;
  END;
/

-- Update Existing data for the first time
UPDATE Branch SET
PhoneNumber = formatPhone(Phone),
FaxNo = formatPhone(FaxNumber);
UPDATE Employee SET
  PhoneNo = formatPhone(PhoneNumber);

-- Drop the old columns on Mar 23 2007
ALTER TABLE Branch DROP COLUMN Phone;
ALTER TABLE Branch DROP COLUMN FaxNumber;
ALTER TABLE Employee DROP COLUMN PhoneNumber;
DROP TRIGGER SynchronizeBranchPhoneNumbers;
DROP TRIGGER SynchronizeEmployeePhoneNumbers;

مکانیک مهاجرت داده (Data-Migration Mechanics)

وقتی تعداد کمی ردیف باید تبدیل شود، احتمالاً یک اسکریپت SQL ساده که ستون(های) هدف را تبدیل می‌کند کافی است. وقتی می‌خواهید حجم زیادی داده تبدیل کنید، یا تبدیل‌های پیچیده دارید، Update Data (صفحهٔ ۳۱۰) را در نظر بگیرید.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی Apply Standard Type اعمال می‌شود، برنامه‌های بیرونی باید به شکل زیر به‌روز شوند:

  1. تغییر نوع دادهٔ متغیرهای برنامه. باید کد برنامه را طوری تغییر دهید که انواع داده با نوع دادهٔ ستون مطابقت داشته باشد.
  2. کد تعامل با پایگاه داده. کدی که داده را از این ستون ذخیره، حذف و بازیابی می‌کند باید با نوع دادهٔ جدید کار کند. مثلاً اگر Customer.Zip از کاراکتر به عددی تغییر کرده، باید کد برنامه را از customerGateway.getString("ZIP") به customerGateway.getLong("ZIP") تغییر دهید.
  3. کد منطق کسب‌وکار. به‌طور مشابه، ممکن است نیاز باشد کد برنامه‌ای که با ستون کار می‌کند به‌روز شود. مثلاً کد مقایسه‌ای مانند Branch.Phone = 'XXX-XXXX' باید به شکلی مانند Branch.Phone = XXXXXXX به‌روز شود.

قطعهٔ کد زیر وضعیت قبل و بعد کلاسی که ردیف Branch را برای یک BranchID داده پیدا می‌کند را نشان می‌دهد، وقتی نوع دادهٔ PhoneNumber از String به Long تغییر می‌کند:

java
// Before code
stmt = DB.prepare("SELECT BranchId, Name, PhoneNumber, " +
             "FaxNumber FROM branch WHERE BranchId = ?");
stmt.setLong(1,findBranchId);
stmt.execute();
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
  rs.getLong("BranchId");
  rs.getString("Name");
  rs.getString("PhoneNumber");
  rs.getString("FaxNumber");
}

// After code
stmt = DB.prepare("SELECT BranchId, Name, PhoneNumber, " +
             "FaxNumber FROM branch WHERE branchId = ?");
stmt.setLong(1,findBranchId);
stmt.execute();
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
  rs.getLong("BranchId");
  rs.getString("Name");
  rs.getLong("PhoneNumber");
  rs.getString("FaxNumber");
}

Consolidate Key Strategy — یکپارچه‌سازی راهبرد کلید

یک راهبرد کلید واحد برای یک موجودیت انتخاب کنید و آن را در سراسر پایگاه داده به‌طور سازگار اعمال کنید.

انگیزه (Motivation)

بسیاری از موجودیت‌های کسب‌وکار چند کلید بالقوه دارند. معمولاً یک یا چند کلید با معنای کسب‌وکار وجود دارد، و اساساً همیشه می‌توانید یک ستون کلید جایگزین (surrogate key) به جدول اختصاص دهید. مثلاً جدول Customer شما ممکن است CustomerPOID به‌عنوان کلید اصلی و هم CustomerNumber و هم SocialSecurityNumber به‌عنوان کلیدهای جایگزین/ثانویه داشته باشد. چند دلیل برای اعمال Consolidate Key Strategy وجود دارد:

  • بهبود عملکرد. احتمالاً برای هر کلید یک index دارید که پایگاه داده برای insert، update و delete با عملکرد مناسب باید آن را نگه دارد.
  • مطابقت با استانداردهای سازمانی. بسیاری از سازمان‌ها راهنماهای دقیق داده و مدل‌سازی دارند که کلیدهای ترجیحی موجودیت‌ها را مشخص می‌کنند. اغلب هنگام اعمال Use Official Data Source (صفحهٔ ۲۷۱) کشف می‌کنید طرحوارهٔ فعلی استانداردها را رعایت نمی‌کند.
  • بهبود سازگاری کد. وقتی برای یک موجودیت انواع مختلفی از کلید دارید، کدی دارید که به روش‌های مختلف به جدول دسترسی پیدا می‌کند. این بار نگهداری را برای کسانی که با آن کد کار می‌کنند افزایش می‌دهد چون باید هر رویکرد را بفهمند.

معاوضه‌های بالقوه (Potential Tradeoffs)

یکپارچه‌سازی راهبرد کلید می‌تواند دشوار باشد. نه‌تنها باید طرحوارهٔ Policy در شکل ۷.۴ را به‌روز کنید، بلکه باید طرحوارهٔ هر جدولی که کلیدهای خارجی به Policy دارد و از راهبرد کلید انتخابی شما استفاده نمی‌کند را هم به‌روز کنید. برای این کار باید Replace Column (صفحهٔ ۱۲۶) را اعمال کنید. ممکن است همچنین کشف کنید مجموعهٔ کلیدهای موجود گزینهٔ مناسبی برای «کلید واحد و حقیقی» ندارد و بنابراین باید Introduce Surrogate Key (صفحهٔ ۸۵) یا Introduce Index (صفحهٔ ۲۴۸) را اعمال کنید.

شکل ۷.۴. یکپارچه‌سازی راهبرد کلید برای جدول Policy.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای پیاده‌سازی این بازآرایی در طرحوارهٔ پایگاه داده باید موارد زیر را انجام دهید:

  1. کلید مناسب را شناسایی کنید. باید دربارهٔ ستون(های) کلید «رسمی» موجودیت توافق کنید. ایده‌آل این است که استانداردهای دادهٔ سازمانی، اگر وجود دارد، را بازتاب دهد.
  2. طرحوارهٔ جدول منبع را به‌روز کنید. ساده‌ترین رویکرد استفاده از کلید اصلی فعلی و توقف استفاده از کلیدهای جایگزین است. با این راهبرد، فقط indexهای پشتیبان کلید را حذف می‌کنید. این رویکرد حتی اگر یکی از کلیدهای جایگزین را به‌جای کلید اصلی فعلی انتخاب کنید هم کار می‌کند. اما اگر هیچ‌یک از کلیدهای موجود مناسب نبود، ممکن است نیاز به Introduce Surrogate Key داشته باشید.
  3. کلیدهای ناخواسته را منسوخ کنید. هر کلید موجودی که قرار نیست کلید اصلی باشد — در این مورد PolicyNumber — باید علامت‌گذاری شود که پس از دورهٔ گذار دیگر به‌عنوان کلید استفاده نمی‌شود. توجه کنید ممکن است بخواهید محدودیت یکتایی روی این ستون‌ها را حفظ کنید، حتی اگر دیگر به‌عنوان کلید استفاده نمی‌شوند.
  4. index جدید اضافه کنید. اگر از قبل وجود ندارد، index جدید مبتنی بر کلید رسمی باید برای Policy از طریق Introduce Index (صفحهٔ ۲۴۸) معرفی شود.

شکل ۷.۴ نشان می‌دهد چگونه راهبرد کلید Policy را برای استفادهٔ تنها از PolicyOID یکپارچه می‌کنیم. برای این کار، Policy.PolicyNumber منسوخ می‌شود تا نشان دهد از ۱۴ دسامبر ۲۰۰۷ دیگر به‌عنوان کلید استفاده نمی‌شود، و PolicyNotes.PolicyOID به‌عنوان ستون کلید جدید برای جایگزینی PolicyNotes.PolicyNumber معرفی می‌شود. کد زیر ستون PolicyNotes.PolicyNumber را اضافه می‌کند:

sql
ALTER TABLE PolicyNotes ADD PolicyOID CHAR(16);

کد زیر پس از پایان دورهٔ گذار اجرا می‌شود تا PolicyNotes.PolicyNumber و index کلید جایگزین مبتنی بر Policy.PolicyNumber را حذف کند:

sql
COMMENT ON Policy 'Consolidation of keys to use only PolicyPOID, dropdate = <<2007-12-14>>'

DROP INDEX PolicyIndex2;

COMMENT ON PolicyNotes 'Consolidation of keys to use only PolicyPOID, therefore drop the PolicyNumber column, dropdate = <<2007-12-14>>'

ALTER TABLE PolicyNotes ADD CONSTRAINT
  PolicyNotesPolciyOID_PK
  PRIMARY KEY (PolicyOID, NoteNumber);

ALTER TABLE PolicyNotes DROP COLUMN PolicyNumber;

مکانیک مهاجرت داده (Data-Migration Mechanics)

جداولی با کلیدهای خارجی که روابط را با Policy نگه می‌دارند باید اکنون کلیدهای خارجی را مطابق راهبرد کلید انتخابی پیاده‌سازی کنند. مثلاً جدول PolicyNotes در ابتدا کلید خارجی مبتنی بر Policy.PolicyNumber داشت. اکنون باید کلید خارجی مبتنی بر Policy.PolicyOID پیاده‌سازی کند. پیامد این است که ممکن است نیاز به اعمال Replace Column (صفحهٔ ۱۲۶) داشته باشید، و این بازآرایی نیاز به کپی داده از ستون منبع (مقدار در Policy.PolicyOID) به ستون PolicyNotes.PolicyOID دارد. کد زیر مقدار ستون PolicyNotes.PolicyNumber را تنظیم می‌کند:

sql
UPDATE PolicyNotes
            SET PolicyNotes.PolicyOID = Policy.PolicyOID
  WHERE PolicyNotes.PolicyNumber = Policy.PolicyNumber;

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی این بازآرایی اعمال می‌شود، وظیفهٔ اصلی اطمینان از این است که عبارت‌های SQL موجود از ستون کلید اصلی رسمی در بندهای WHERE استفاده کنند تا عملکرد خوب joinها حفظ شود. مثلاً کد قبل جداول Policy، PolicyNotes و PolicyDueDiligence را با ترکیبی از ستون‌های PolicyOID و PolicyNumber join می‌کرد. کد «بعد» آن‌ها را فقط بر اساس ستون PolicyOID join می‌کند:

java
// Before code
stmt.prepare(
 "SELECT Policy.Note FROM Policy, PolicyNotes " +
 "WHERE Policy.PolicyNumber = PolicyNotes.PolicyNumber "+
 "AND Policy.PolicyOID=?");
stmt.setLong(1,policyOIDToFind);
stmt.execute();
ResultSet rs = stmt.executeQuery();

// After code
stmt.prepare(
 "SELECT Policy.Note FROM Policy, PolicyNotes " +
 "WHERE Policy.PolicyOID = PolicyNotes.PolicyOID "+
 "AND Policy.PolicyOID=?");
stmt.setLong(1,policyOIDToFind);
stmt.execute();
ResultSet rs = stmt.executeQuery();

Drop Column Constraint — حذف محدودیت ستون

محدودیت ستون را از یک جدول موجود حذف کنید.

وقتی نیاز به حذف محدودیت یکپارچگی ارجاعی روی ستون دارید، استفاده از Drop Foreign Key Constraint (صفحهٔ ۲۱۳) را در نظر بگیرید.

انگیزه (Motivation)

رایج‌ترین دلیل اعمال Drop Column Constraint این است که محدودیت دیگر به‌دلیل تغییر قواعد کسب‌وکار قابل‌اجرا نیست. مثلاً شاید ستون Address.Country فعلاً به مقادیر "US" و "Canada" محدود شده، اما اکنون بین‌المللی کار می‌کنید و آدرس‌ها در سراسر جهان دارید. دلیل دوم این است که محدودیت فقط به زیرمجموعه‌ای از برنامه‌هایی که به ستون دسترسی دارند اعمال می‌شود — شاید به‌دلیل تغییر برخی برنامه‌ها اما نه همه. مثلاً برخی برنامه‌ها در محیط‌های بین‌المللی استفاده می‌شوند، در حالی که برخی هنوز فقط آمریکای شمالی هستند — چون محدودیت دیگر برای همهٔ برنامه‌ها قابل‌اجرا نیست، باید از پایگاه دادهٔ مشترک حذف و در برنامه‌هایی که واقعاً به آن نیاز دارند پیاده‌سازی شود. به‌عنوان اثر جانبی، ممکن است بهبود عملکرد جزئی ببینید چون پایگاه داده دیگر نیازی به اعمال محدودیت ندارد. دلیل سوم این است که ستون بازآرایی شده — شاید Apply Standard Codes (صفحهٔ ۱۵۷) یا Apply Standard Type (صفحهٔ ۱۶۲) روی آن اعمال شده — و اکنون باید محدودیت ستون حذف، بازآرایی و سپس از طریق Introduce Column Constraint (صفحهٔ ۱۸۰) دوباره اعمال شود.

معاوضه‌های بالقوه (Potential Tradeoffs)

چالش اصلی این بازآرایی این است که ممکن است نیاز باشد منطق محدودیت را در زیرمجموعهٔ برنامه‌هایی که به آن نیاز دارند پیاده‌سازی کنید. چون همان منطق را در چند جا پیاده‌سازی می‌کنید، خطر پیاده‌سازی به روش‌های مختلف وجود دارد.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای به‌روزرسانی طرحوارهٔ پایگاه داده، باید محدودیت را از Balance با بند DROP CONSTRAINT دستور SQL ALTER TABLE حذف کنید. شکل ۷.۵ مثالی را نشان می‌دهد که محدودیت این است ستون Account.Balance مثبت باشد؛ این محدودیت برای پشتیبانی از امکان افت موجودی (overdrawn) حساب‌ها حذف می‌شود. کد زیر DDL مربوط به حذف محدودیت روی ستون Account.Balance را نشان می‌دهد:

شکل ۷.۵. حذف محدودیت ستون از جدول Account.

sql
ALTER TABLE Account DROP CONSTRAINT Positive_Balance;

مکانیک مهاجرت داده (Data-Migration Mechanics)

برای این بازآرایی پایگاه داده داده‌ای برای مهاجرت وجود ندارد.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

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

Drop Default Value — حذف مقدار پیش‌فرض

مقدار پیش‌فرضی که پایگاه داده برای یک ستون جدول موجود فراهم می‌کند را حذف کنید.

انگیزه (Motivation)

اغلب وقتی می‌خواهیم پایگاه داده برخی ستون‌ها را با داده ذخیره کند و برنامه داده‌ای به آن ستون‌ها اختصاص نمی‌دهد، از Introduce Default Value (صفحهٔ ۱۸۶) استفاده می‌کنیم. وقتی دیگر نیازی نداریم پایگاه داده برای برخی ستون‌ها داده درج کند — چون برنامه دادهٔ لازم را فراهم می‌کند — در بسیاری موارد دیگر نمی‌خواهیم پایگاه داده مقدار پیش‌فرض را ذخیره کند، چون می‌خواهیم برنامه مقدار ستون را فراهم کند. در این موقعیت از بازآرایی Drop Default Value استفاده می‌کنیم.

معاوضه‌های بالقوه (Potential Tradeoffs)

دو چالش بالقوه دربارهٔ این بازآرایی وجود دارد. اول، ممکن است اثرات جانبی ناخواسته داشته باشد. برخی برنامه‌ها ممکن است فرض کنند پایگاه داده مقدار پیش‌فرض را ذخیره می‌کند و بنابراین رفتار متفاوتی نشان دهند — ستون‌هایی در ردیف‌های جدید که قبلاً مقداری داشتند اکنون null هستند. دوم، باید مدیریت استثنا برنامه‌های بیرونی را بهبود دهید که ممکن است بیش از ارزشش تلاش باشد. اگر ستون non-nullable باشد و برنامه داده ارائه ندهد، پایگاه داده استثنا پرتاب می‌کند که برنامه انتظارش را ندارد.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای به‌روزرسانی طرحواره و حذف مقدار پیش‌فرض، باید مقدار پیش‌فرض را از ستون جدول در پایگاه داده با بند MODIFY دستور ALTER TABLE حذف کنید. کد زیر گام‌های حذف مقدار پیش‌فرض روی ستون Customer.Status شکل ۷.۶ را نشان می‌دهد. از دید پایگاه داده، مقدار پیش‌فرض null همانند نداشتن مقدار پیش‌فرض است:

شکل ۷.۶. حذف مقدار پیش‌فرض برای ستون Customer.Status.

sql
ALTER TABLE Customer MODIFY Status DEFAULT NULL;

مکانیک مهاجرت داده (Data-Migration Mechanics)

برای این بازآرایی پایگاه داده داده‌ای برای مهاجرت وجود ندارد.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

اگر برخی برنامه‌های دسترسی به مقدار پیش‌فرض وابسته‌اند، یا باید کد اعتبارسنجی داده اضافه کنید تا با تغییر جدول مقابله کند، یا از این بازآرایی صرف‌نظر کنید.

کد زیر نشان می‌دهد کد برنامه اکنون باید مقدار ستون را فراهم کند به‌جای اینکه به پایگاه داده برای مقدار پیش‌فرض وابسته باشد:

java
// Before code
public void createRetailCustomer
  (long customerId,String firstName) {
  stmt = DB.prepare("INSERT into customer" +
    "(Customerid, FirstName) " +
    "values (?, ?)");
  stmt.setLong(1, customerId);
  stmt.setString(2, firstName);
  stmt.execute();
}

// After code
public void createRetailCustomer
  (long customerId,String firstName) {
  stmt = DB.prepare("INSERT into customer" +
    "(Customerid, FirstName, Status) " +
    "values (?, ?, ?)");
  stmt.setLong(1, customerId);
  stmt.setString(2, firstName);
  stmt.setString(3, RETAIL);
  stmt.execute();
}

Drop Non-Nullable Constraint — حذف محدودیت غیرقابل تهی

یک ستون non-nullable موجود را طوری تغییر دهید که مقادیر null را بپذیرد.

شکل ۷.۷. nullable کردن ستون CityLookup.StateID.

انگیزه (Motivation)

دو دلیل اصلی برای اعمال این بازآرایی وجود دارد. اول، فرایند کسب‌وکار تغییر کرده و اکنون بخش‌هایی از موجودیت در زمان‌های مختلف ذخیره می‌شوند. مثلاً یک برنامه موجودیت را ایجاد می‌کند، فوراً به این ستون مقداری اختصاص نمی‌دهد، و برنامهٔ دیگری بعداً ردیف را به‌روز می‌کند. دوم، در دوره‌های گذار ممکن است بخواهید ستون خاصی nullable باشد. مثلاً وقتی یکی از برنامه‌ها به‌دلیل بازآرایی‌ای که در حال انجام است نمی‌تواند مقداری برای ستون فراهم کند، می‌خواهید محدودیت non-nullable را برای مدت محدودی در فاز گذار تغییر دهید تا برنامه بتواند کار کند. بعداً Make Column Non-Nullable (صفحهٔ ۱۸۹) را اعمال می‌کنید تا محدودیت به حالت قبل برگردد.

معاوضه‌های بالقوه (Potential Tradeoffs)

هر برنامه‌ای که به این ستون دسترسی دارد باید بتواند مقدار null را بپذیرد — حتی اگر فقط نادیده بگیرد یا احتمالاً هنگام کشف null بودن ستون، یک پیش‌فرض هوشمندانه فرض کند. اگر مقدار پیش‌فرض هوشمندانه‌ای وجود دارد، بازآرایی Introduce Default Value (صفحهٔ ۱۸۶) را هم در نظر بگیرید.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای انجام این بازآرایی باید محدودیت NOT NULL را از ستون حذف کنید. این کار با دستور SQL ALTER TABLE MODIFY COLUMN انجام می‌شود. کد زیر نحوهٔ nullable کردن CityLookup.StateCode را نشان می‌دهد:

sql
-- On 2007-06-14
ALTER TABLE CityLookup MODIFY StateCode NULL;

مکانیک مهاجرت داده (Data-Migration Mechanics)

برای این بازآرایی پایگاه داده داده‌ای برای مهاجرت وجود ندارد.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

باید همهٔ برنامه‌های بیرونی که فعلاً به CityLookup.StateCode دسترسی دارند را بازآرایی کنید تا کد بتواند مقادیر null را به‌درستی مدیریت کند. چون مقادیر null قابل ذخیره‌اند، می‌توانند بازیابی شوند — یعنی باید کد بررسی مقدار null اضافه کنیم. علاوه بر این، می‌خواهید هر کدی که استثناهای مقدار null را بررسی می‌کند — که دیگر توسط پایگاه داده پرتاب نمی‌شوند — بازنگری شود.

نمونهٔ کد زیر نحوهٔ افزودن منطق بررسی null به کد برنامه را نشان می‌دهد:

java
// Before code
public StringBuffer getAddressString(Address address) {
  StringBuffer stringAddress = new StringBuffer();
  stringAddress = address.getStreetLine1();
  stringAddress.append(address.getStreetLine2());
  stringAddress.append(address.getCity());
  stringAddress.append(address.getPostalCode());
  stringAddress.append(states.getNameFor
  (address.getStateCode()));
  return stringAddress;
}

// After code
public StringBuffer getAddressString(Address address) {
  StringBuffer stringAddress = new StringBuffer();
  stringAddress = address.getStreetLine1();
  stringAddress.append(address.getStreetLine2());
  stringAddress.append(address.getCity());
  stringAddress.append(address.getPostalCode());
  String statecode = address.getStateCode;
  if (statecode != null) {
    stringAddress.append(states.getNameFor
    (stateCode()));
  }
  return stringAddress;
}

Introduce Column Constraint — معرفی محدودیت ستون

محدودیت ستون را در یک جدول موجود معرفی کنید.

وقتی نیاز به افزودن محدودیت non-nullable روی ستون دارید، استفاده از بازآرایی Make Column Non-Nullable (صفحهٔ ۱۸۹) را در نظر بگیرید. وقتی نیاز به افزودن محدودیت یکپارچگی ارجاعی روی ستون دارید، استفاده از Add Foreign Key (صفحهٔ ۲۰۴) را در نظر بگیرید.

انگیزه (Motivation)

رایج‌ترین دلیل اعمال Introduce Column Constraint اطمینان از این است که همهٔ برنامه‌هایی که با پایگاه داده تعامل دارند دادهٔ معتبر در ستون ذخیره کنند. به‌عبارت دیگر، محدودیت‌های ستون معمولاً برای پیاده‌سازی قواعد اعتبارسنجی دادهٔ مشترک و نسبتاً ساده استفاده می‌شوند.

معاوضه‌های بالقوه (Potential Tradeoffs)

مسئلهٔ اصلی این است که آیا واقعاً محدودیت مشترکی برای این عنصر داده در همهٔ برنامه‌هایی که به آن دسترسی دارند وجود دارد. برنامه‌های فردی ممکن است نسخهٔ منحصربه‌فرد خود از محدودیت برای این ستون داشته باشند. مثلاً جدول Customer می‌تواند ستون FavoriteColor داشته باشد. استاندارد سازمانی شما ممکن است مقادیر "Red"، "Green" و "Blue" باشد، اما به دلایل رقابتی یک برنامه ممکن است "Yellow" را به‌عنوان مقدار چهارم بپذیرد، در حالی که دو برنامهٔ دیگر فقط "Red" و "Blue" را می‌پذیرند. می‌توان گفت باید یک استاندارد سازگار در همهٔ برنامه‌ها باشد، اما واقعیت این است که برنامه‌های فردی دلایل خوبی برای کار به شیوهٔ خاص خود دارند. پیامد این است که چون برنامه‌های فردی نسخه‌های خود از قواعد کسب‌وکار را پیاده‌سازی می‌کنند، حتی در بهترین شرایط کشف می‌کنید دادهٔ موجود در پایگاه داده شرایط محدودیت را برآورده نمی‌کند. یک راهبرد نوشتن اسکریپتی است که جداول پایگاه داده را مرور کند و هر نقض محدودیت را گزارش دهد و دادهٔ نیازمند اصلاح را شناسایی کند. این فرایندها در حالت batch در ساعات غیرشلوغ پایگاه داده و برنامه اجرا می‌شوند.

همچنین ممکن است این بازآرایی را به‌دلیل نیاز به بازآرایی محدودیت ستون موجود اعمال کنید. بازآرایی‌هایی مانند Apply Standard Codes (صفحهٔ ۱۵۷) و Apply Standard Type (صفحهٔ ۱۶۲) ممکن است مجبورتان کنند مقدار کد یا نوع دادهٔ زیربنایی ستون را تغییر دهید و محدودیت را دوباره معرفی کنید.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

همان‌طور که در شکل ۷.۸ نشان داده شده، برای به‌روزرسانی طرحوارهٔ پایگاه داده باید محدودیت را روی ستون معرفی کنید — در این مورد Customer.CreditLimit. همان‌طور که در کد زیر می‌بینید، این کار با بند ADD CONSTRAINT دستور SQL ALTER TABLE انجام می‌شود. در این مورد، سقف اعتبار باید کمتر از ۵۰٬۰۰۰ دلار باشد به‌عنوان تاکتیک پیشگیری از تقلب:

شکل ۷.۸. معرفی محدودیت روی ستون Customer.CreditLimit.

sql
ALTER TABLE Customer ADD CONSTRAINT
  Check_Credit_Limit CHECK (CreditLimit < 50000.00);

مکانیک مهاجرت داده (Data-Migration Mechanics)

اگرچه به‌معنای دقیق مهاجرت داده نیست، چالش اصلی این بازآرایی اطمینان از این است که دادهٔ موجود با محدودیت اعمال‌شده روی ستون مطابقت دارد. ابتدا باید محدودیت را با همکاری ذی‌نفعان پروژه تعریف کنید تا مشخص شود با مقادیر داده‌ای که با محدودیت مطابقت ندارند چه باید کرد. سپس باید دادهٔ منبع را اصلاح کنید. ممکن است نیاز به اعمال Update Data (صفحهٔ ۳۱۰) داشته باشید تا مقادیر ذخیره‌شده در این ستون با محدودیت مطابقت داشته باشند.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

باید اطمینان حاصل کنید که برنامه‌های دسترسی می‌توانند خطاهایی که پایگاه داده هنگام نوشتن داده‌ای که با محدودیت مطابقت ندارد پرتاب می‌کند را مدیریت کنند. باید هر نقطه‌ای در برنامه‌ها که insert یا update روی جدول انجام می‌شود را پیدا کنید و کد مدیریت استثنای مناسب اضافه کنید، همان‌طور که در کد زیر نشان داده شده:

java
// Before code
stmt = conn.prepare(
  "INSERT INTO Customer "+
  "(CustomerID,FirstName,Status,CreditLimit) "+
  "VALUES (?,?,?,?)");
stmt.setLong(1,customerId);
stmt.setString(2,firstName);
stmt.setString(3,status);
stmt.setBigDecimal(4,creditLimit);
stmt.executeUpdate();
}

// After code
stmt = conn.prepare(
  "INSERT INTO Customer "+
  "(CustomerID,FirstName,Status,CreditLimit) "+
  "VALUES (?,?,?,?)");
stmt.setLong(1,customerId);
stmt.setString(2,firstName);
stmt.setString(3,status);
stmt.setBigDecimal(4,creditLimit);
try {
  stmt.executeUpdate();
} catch (SQLException exception){
    while (exception != null) {
      int errorCode = e.getErrorCode();
      if (errorCode = 2290) {
            handleCreditLimitExceeded();
      }
    }
}

Introduce Common Format — معرفی قالب مشترک

قالب سازگاری را روی همهٔ مقادیر داده در یک ستون جدول موجود اعمال کنید.

شکل ۷.۹. اعمال قالب مشترک روی ستون Address.PhoneNumber.

انگیزه (Motivation)

معمولاً Introduce Common Format را برای ساده‌سازی کد برنامهٔ بیرونی اعمال می‌کنید. وقتی همان داده در قالب‌های مختلف ذخیره شده، برنامه‌های بیرونی به منطق پیچیدهٔ غیرضروری برای کار با داده نیاز دارند. به‌طور کلی بهتر است داده در قالب یکنواخت باشد تا برنامه‌هایی که با پایگاه داده رابط دارند مجبور نباشند چند قالب را مدیریت کنند. مثلاً وقتی مقادیر Customer.Phonenumber برابر '4163458899'، '905-777-8889' و '(990) 345-6666' هستند، هر برنامه‌ای که به آن ستون دسترسی دارد باید هر سه قالب را parse کند. این مشکل وقتی بدتر می‌شود که برنامهٔ بیرونی داده را در چند جا parse می‌کند — اغلب به‌دلیل طراحی ضعیف برنامه. قالب مشترک به شما امکان می‌دهد کد اعتبارسنجی مربوط به Customer.Phonenumber را کاهش دهید و پیچیدگی منطق نمایش را کم کنید — اگرچه داده به‌صورت 1234567890 ذخیره می‌شود، ممکن است در گزارش به‌صورت (123) 456-7890 نمایش داده شود.

دلیل رایج دیگر مطابقت با استانداردهای سازمانی موجود است. اغلب هنگام اعمال Use Official Data Source (صفحهٔ ۲۷۱) کشف می‌کنید قالب دادهٔ فعلی با منبع دادهٔ «رسمی» متفاوت است و بنابراین داده باید برای سازگاری بازقالب‌بندی شود.

معاوضه‌های بالقوه (Potential Tradeoffs)

استانداردسازی قالب مقادیر داده می‌تواند دشوار باشد وقتی چند قالب در یک ستون وجود دارد. مثلاً ستون Customer.Phonenumber ممکن است با ۱۵ قالب مختلف ذخیره شده باشد — به کدی نیاز دارید که قالب هر ردیف را تشخیص دهد و به قالب استاندارد تبدیل کند. خوشبختانه کد تبدیل احتمالاً از قبل در یک یا چند برنامهٔ بیرونی وجود دارد.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای اعمال Introduce Common Format روی ستون، ابتدا باید قالب استاندارد را شناسایی کنید. باید دربارهٔ قالب «رسمی» مقادیر داده توافق کنید. قالب داده ممکن است استاندارد موجود باشد یا استاندارد جدیدی که ذی‌نفعان کسب‌وکار تعریف کرده‌اند — در هر صورت باید توسط ذی‌نفع(های) پروژه پذیرفته شود. سپس باید ستون(هایی) که قالب باید روی آن‌ها اعمال شود را شناسایی کنید. Introduce Common Format یک ستون در هر بار اعمال می‌شود — بازآرایی‌های کوچک‌تر همیشه پیاده‌سازی و استقرار در تولید آسان‌ترند. ممکن است چند بار برای سازگاری در سراسر پایگاه داده نیاز باشد.

مکانیک مهاجرت داده (Data-Migration Mechanics)

گام اول شناسایی قالب‌های مختلف فعلاً استفاده‌شده در ستون است — اغلب یک عبارت SELECT ساده این اطلاعات را می‌دهد تا بفهمید کد مهاجرت داده چه کاری باید انجام دهد. گام دوم نوشتن کدی برای تبدیل دادهٔ موجود به قالب استاندارد است. می‌توانید این کد را با DML استاندارد SQL، زبان برنامه‌نویسی مانند Java یا C#، یا ابزار ETL بنویسید. وقتی تعداد کمی ردیف به‌روز می‌شود، اسکریپت SQL ساده کافی است. وقتی حجم زیادی داده به‌روز می‌کنید، یا کد در جداول تراکنشی تغییر می‌کند، Update Data (صفحهٔ ۳۱۰) را اعمال کنید.

کد زیر DML مربوط به به‌روزرسانی داده در جدول Customer را نشان می‌دهد:

sql
UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,'-','');

UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,' ','');

UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,'(','');

UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,')','');

UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,'+1','');

UPDATE Customer SET PhoneNumber =
      REPLACE(PhoneNumber,'.','');

همان‌طور که می‌بینید، هر بار یک نوع قالب را به‌روز می‌کنیم. همچنین می‌توانید تغییرات فردی را با stored procedure کپسوله کنید:

sql
UPDATE Address SET PhoneNumber =
FormatPhoneNumber(PhoneNumber);

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی Introduce Common Format اعمال می‌شود، جنبه‌های زیر برنامه‌های بیرونی باید بررسی شوند:

  1. کد پاک‌سازی. برنامه‌های بیرونی منطقی دارند که قالب‌های مختلف را می‌پذیرد و به قالب مورد نظر تبدیل می‌کند. ممکن است بتوانید بخشی از این کد موجود را به‌عنوان پایهٔ کد مهاجرت داده بالا استفاده کنید.
  2. کد اعتبارسنجی. ممکن است نیاز باشد کد منبعی که مقادیر ویژگی‌های داده را اعتبارسنجی می‌کند به‌روز شود. مثلاً کدی که به دنبال قالب‌هایی مانند Phonenumber = 'NNN-NNN-NNNN' می‌گردد باید برای قالب دادهٔ جدید به‌روز شود.
  3. کد نمایش. کد رابط کاربری اغلب منطقی برای نمایش عناصر داده در قالب خاص دارد — اغلب قالبی که برای ذخیره استفاده نمی‌شود (مثلاً NNNNNNNNNN برای ذخیره، (NNN) NNNNNNN برای نمایش). این شامل منطق نمایش برای صفحه‌ها و گزارش‌ها می‌شود.
  4. دادهٔ آزمون. دادهٔ آزمون یا کد تولید دادهٔ آزمون باید برای مطابقت با قالب دادهٔ استاندارد جدید تغییر کند. ممکن است بخواهید موارد تست جدیدی برای ردیف‌های داده با قالب نامناسب اضافه کنید.

Introduce Default Value — معرفی مقدار پیش‌فرض

بگذارید پایگاه داده برای یک ستون جدول موجود مقدار پیش‌فرض فراهم کند.

انگیزه (Motivation)

اغلب می‌خواهیم وقتی ردیف جدیدی به جدول اضافه می‌شود، ستون مقدار پیش‌فرض داشته باشد. اما عبارت‌های insert همیشه آن ستون را پر نمی‌کنند — اغلب چون ستون پس از نوشتن insert اصلی اضافه شده یا کد برنامه‌ای که insert را ارسال می‌کند به آن ستون نیاز ندارد. به‌طور کلی Introduce Default Value وقتی مفید است که بخواهیم بعداً ستون را non-nullable کنیم (بازآرایی Make Column Non-Nullable را در صفحهٔ ۱۸۹ ببینید).

معاوضه‌های بالقوه (Potential Tradeoffs)

چند چالش بالقوه دربارهٔ این بازآرایی وجود دارد:

  • شناسایی پیش‌فرض واقعی می‌تواند دشوار باشد. وقتی چند برنامه پایگاه دادهٔ مشترک دارند، ممکن است مقادیر پیش‌فرض متفاوتی برای همان ستون داشته باشند — اغلب به دلایل خوب. یا شاید ذی‌نفعان کسب‌وکار نتوانند روی یک مقدار واحد توافق کنند — باید نزدیک با آن‌ها برای مذاکرهٔ مقدار صحیح کار کنید.
  • اثرات جانبی ناخواسته. برخی برنامه‌ها ممکن است فرض کنند مقدار null در ستون واقعاً معنایی دارد و بنابراین رفتار متفاوتی نشان دهند — ستون‌هایی در ردیف‌های جدید که قبلاً null بودند اکنون null نیستند.
  • زمینهٔ گیج‌کننده. وقتی برنامه‌ای از ستون استفاده نمی‌کند، مقدار پیش‌فرض ممکن است دربارهٔ کاربرد ستون برای تیم برنامه سردرگمی ایجاد کند.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

این بازآرایی تک‌مرحله‌ای است. فقط از دستور SQL ALTER TABLE برای تعریف مقدار پیش‌فرض ستون استفاده می‌کنید. تاریخ مؤثر برای این بازآرایی اختیاری است تا به مردم بگوید این مقدار پیش‌فرض از چه زمانی در طرحواره معرفی شده. کد زیر نحوهٔ معرفی مقدار پیش‌فرض روی ستون Customer.Status را نشان می‌دهد، همان‌طور که در شکل ۷.۱۰ نشان داده شده:

شکل ۷.۱۰. معرفی مقدار پیش‌فرض روی ستون Customer.Status.

sql
ALTER TABLE Customer MODIFY Status DEFAULT 'NEW';

COMMENT ON Customer.Status 'Default value of NEW will be inserted when there is no data
 present
on the insert as of June 14 2006';

مکانیک مهاجرت داده (Data-Migration Mechanics)

ردیف‌های موجود ممکن است از قبل مقادیر null در این ستون داشته باشند — ردیف‌هایی که به‌طور خودکار با افزودن مقدار پیش‌فرض به‌روز نمی‌شوند. علاوه بر این، ممکن است مقادیر نامعتبر در برخی ردیف‌ها هم باشد. باید مقادیر موجود در ستون را بررسی کنید — نگاه به فهرست یکتای مقادیر ممکن است کافی باشد — تا مشخص کنید آیا به‌روزرسانی لازم است. در صورت لزوم، اسکریپتی بنویسید که جدول را مرور کند و مقدار پیش‌فرض را به این ردیف‌ها اضافه کند.

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

در ظاهر بعید به نظر می‌رسد برنامه‌های دسترسی تحت تأثیر معرفی مقدار پیش‌فرض قرار گیرند؛ اما ظاهر می‌تواند فریبنده باشد. مسائل بالقوه عبارت‌اند از:

  1. ناپایداری‌ها (invariants) با مقدار جدید شکسته می‌شوند. مثلاً کلاسی ممکن است فرض کند مقدار ستون رنگ red، green یا blue است، اما مقدار پیش‌فرض اکنون yellow تعریف شده.
  2. کد اعمال مقادیر پیش‌فرض وجود دارد. ممکن است کد منبع اضافی وجود داشته باشد که null را بررسی می‌کند و برنامه‌وار مقدار پیش‌فرض معرفی می‌کند. این کد باید حذف شود.
  3. کد منبع موجود مقدار پیش‌فرض متفاوتی فرض می‌کند. مثلاً کد موجود ممکن است به دنبال مقدار پیش‌فرض none بگردد که در گذشته برنامه‌وار تنظیم می‌شد، و اگر پیدا شود به کاربر امکان تغییر رنگ می‌دهد. اکنون مقدار پیش‌فرض yellow است، پس این کد هرگز فراخوانی نمی‌شود.

باید برنامه‌های دسترسی را به‌طور کامل تحلیل کنید و قبل از معرفی مقدار پیش‌فرض روی ستون، آن‌ها را به‌روز کنید.

Make Column Non-Nullable — غیرقابل تهی کردن ستون

ستون موجود را طوری تغییر دهید که هیچ مقدار nullی نپذیرد.

انگیزه (Motivation)

دو دلیل برای اعمال Make Column Non-Nullable وجود دارد. اول، می‌خواهید قواعد کسب‌وکار را در سطح پایگاه داده اعمال کنید تا هر برنامه‌ای که این ستون را به‌روز می‌کند مجبور به ارائهٔ مقدار باشد. دوم، می‌خواهید منطق تکراری در برنامه‌ها که بررسی not-null انجام می‌دهد حذف شود — اگر مجاز به درج null نباشید، هرگز null دریافت نمی‌کنید.

معاوضه‌های بالقوه (Potential Tradeoffs)

هر برنامهٔ بیرونی که ردیف‌ها را در جدول داده به‌روز می‌کند باید مقداری برای ستون فراهم کند — برخی برنامه‌ها ممکن است فعلاً فرض کنند ستون nullable است و بنابراین چنین مقداری ارائه ندهند. هر بار insert یا update باید اطمینان حاصل کنید مقداری ارائه شده — یعنی برنامه‌های بیرونی باید به‌روز شوند و/یا خود پایگاه داده باید مقدار معتبر فراهم کند. یک تکنیک مفید که یافته‌ایم استفاده از Introduce Default Value (صفحهٔ ۱۸۶) برای این ستون است.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

همان‌طور که در شکل ۷.۱۱ نشان داده شده، برای انجام Make Column Non-Nullable باید محدودیت NOT NULL را به ستون اضافه کنید. این کار با دستور SQL ALTER TABLE انجام می‌شود، همان‌طور که در کد زیر نشان داده شده:

شکل ۷.۱۱. غیرقابل تهی کردن ستون Customer.FirstName.

sql
ALTER TABLE Customer
  MODIFY FirstName NOT NULL;

نکتهٔ سبکی — نمایش سادهٔ مدل‌ها: شکل ۷.۱۱ یک مسئلهٔ سبکی جالب نشان می‌دهد — در طرحوارهٔ اصلی نشان نمی‌دهد FirstName nullable است. چون رایج است فرض شود ستون‌هایی که بخشی از کلید اصلی نیستند nullable هستند، افزودن stereotype <<nullable>> روی بیشتر ستون‌ها فقط نمودار را شلوغ می‌کند. نمودارهای شما وقتی ساده‌تر نشان داده شوند خواناترند.

مکانیک مهاجرت داده (Data-Migration Mechanics)

ممکن است نیاز به پاک‌سازی دادهٔ موجود داشته باشید چون نمی‌توانید ستون را non-nullable کنید اگر ردیف‌های موجودی با مقدار null در ستون وجود داشته باشد. برای حل این مشکل، اسکریپتی بنویسید که ردیف‌های حاوی null را با مقدار مناسب جایگزین کند.

کد زیر نحوهٔ پاک‌سازی دادهٔ موجود برای پشتیبانی از تغییر نشان‌داده‌شده در شکل ۷.۱۱ را نشان می‌دهد. گام اول اطمینان از این است که ستون FirstName هیچ مقدار nullی ندارد؛ اگر دارد، باید داده در جدول به‌روز شود:

sql
SELECT count(FirstName) FROM Customer
  WHERE
    FirstName IS NULL;

اگر ردیف‌هایی پیدا کنیم که Customer.FirstName null است، باید الگوریتمی اعمال کنیم و اطمینان حاصل کنیم FirstName هیچ nullی ندارد. در این مثال، Customer.FirstName را به '???' تنظیم می‌کنیم تا نشان دهیم این رکورد نیاز به به‌روزرسانی دارد. این راهبرد با دقت توسط ذی‌نفعان پروژه انتخاب شد چون دادهٔ در حال تغییر برای کسب‌وکار حیاتی است:

sql
UPDATE Customer SET FirstName='???'
  WHERE
    FirstName IS NULL;

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

باید همهٔ برنامه‌های بیرونی که فعلاً به Customer.FirstName دسترسی دارند را بازآرایی کنید تا هر بار ردیفی در جدول تغییر می‌دهند مقدار مناسب ارائه دهند. علاوه بر این، باید استثناهای جدیدی که پایگاه داده پرتاب می‌کند را تشخیص داده و مدیریت کنند.

اگر مطمئن نیستید این ستون همه‌جا کجا استفاده شده و راهی برای تغییر همهٔ موارد ندارید، می‌توانید بازآرایی Introduce Default Value (صفحهٔ ۱۸۶) را اعمال کنید تا پایگاه داده وقتی برنامه مقداری ارائه نمی‌دهد مقدار پیش‌فرض بدهد. این ممکن است راهبرد موقت باشد — پس از گذشت زمان کافی و اطمینان از به‌روزرسانی برنامه‌های دسترسی، یا حداقل تا آمادگی برای رسیدگی سریع به برنامه‌هایی که به‌روز نشده‌اند، می‌توانید Drop Default Value (صفحهٔ ۱۷۴) را اعمال کنید و عملکرد را بهبود دهید.

کد زیر وضعیت قبل و بعد را نشان می‌دهد وقتی Make Column Non-Nullable اعمال می‌شود. در مثال، تصمیم گرفته‌ایم هنگام یافتن مقادیر null استثنا پرتاب کنیم:

java
// Before code
stmt = conn.prepare(
  "INSERT INTO Customer "+
  "(CustomerID,FirstName,Surname) "+
  "VALUES (?,?,?,)");
stmt.setLong(1,customerId);
stmt.setString(2,firstName);
stmt.setString(3,surname);
stmt.executeUpdate();
}

// After code
if (firstName == null) {
   throw new CustomerFirstNameCannotBeNullException();
};
stmt = conn.prepare(
   "INSERT INTO Customer "+
   "(CustomerID,FirstName,Surname) "+
   "VALUES (?,?,?,)");
stmt.setLong(1,customerId);
stmt.setString(2,firstName);
stmt.setString(3,surname);
stmt.executeUpdate();
}

Move Data — انتقال داده

دادهٔ موجود در یک جدول — همه یا زیرمجموعه‌ای از ستون‌های آن — را به جدول موجود دیگری منتقل کنید.

انگیزه (Motivation)

معمولاً Move Data را در نتیجهٔ تغییرات ساختاری در طراحی جدول اعمال می‌کنید؛ اما همان‌طور که همیشه، اعمال این بازآرایی به موقعیت بستگی دارد. ممکن است به دلایل زیر Move Data را اعمال کنید:

  • تغییر نام ستون. وقتی ستون را تغییر نام می‌دهید، فرایند ابتدا معرفی ستون جدید با نام جدید، انتقال دادهٔ اصلی به ستون جدید، و سپس حذف ستون اصلی با Drop Column (صفحهٔ ۷۲) است.
  • تقسیم عمودی جدول. وقتی Split Table (صفحهٔ ۱۴۵) را برای سازمان‌دهی مجدد عمودی جدول موجود اعمال می‌کنید، باید داده را از جدول منبع به جداول جدیدی که از آن جدا شده‌اند منتقل کنید.
  • تقسیم افقی جدول. گاهی برشی افقی از داده از جدولی به جدول دیگر با همان ساختار منتقل می‌شود چون جدول اصلی آن‌قدر بزرگ شده که عملکرد افت کرده. مثلاً ممکن است همهٔ ردیف‌های جدول Customer که مشتریان اروپایی را نشان می‌دهند به جدول EuropeanCustomer منتقل کنید. این تقسیم افقی ممکن است گام اول به سمت ساخت سلسله‌مراتب وراثت (مثلاً EuropeanCustomer از Customer ارث می‌برد) در پایگاه داده و/یا به‌دلیل قصد افزودن ستون‌های خاص برای اروپایی‌ها باشد.
  • تقسیم ستون. با Split Column (صفحهٔ ۱۴۰)، باید داده را به ستون(های) جدید از منبع منتقل کنید.
  • ادغام جدول یا ستون. اعمال Merge Tables (صفحهٔ ۹۶) یا Merge Columns (صفحهٔ ۹۲) نیاز به انتقال داده از منبع(ها) به هدف(ها) دارد.
  • یکپارچه‌سازی داده بدون تغییرات ساختاری. داده اغلب در چند مکان ذخیره می‌شود. مثلاً ممکن است چند جدول مشتری‌محور داشته باشید — یکی برای اطلاعات مشتریان کانادایی و چند تا برای مشتریان مناطق مختلف آمریکا — همه با ساختار پایهٔ یکسان. در نتیجهٔ سازمان‌دهی مجدد شرکتی، دادهٔ استان‌های British Columbia و Alberta از جدول CanadianCustomer به جدول WestCoastCustomer منتقل می‌شود تا مالکیت داده در سازمان بازتاب یابد.
  • انتقال داده قبل از حذف جدول یا ستون. اعمال Drop Table (صفحهٔ ۷۷) یا Drop Column (صفحهٔ ۷۲) ممکن است ابتدا نیاز به Move Data داشته باشد اگر بخشی یا همهٔ دادهٔ ذخیره‌شده در جدول یا ستون هنوز لازم است.

معاوضه‌های بالقوه (Potential Tradeoffs)

انتقال داده بین ستون‌ها همیشه می‌تواند دشوار باشد، اما وقتی میلیون‌ها ردیف درگیر است به‌ویژه چالش‌برانگیز است. در طول انتقال، برنامه‌هایی که به داده دسترسی دارند ممکن است تحت تأثیر قرار گیرند — احتمالاً می‌خواهید داده را در طول انتقال قفل کنید — که بر عملکرد و در دسترس بودن داده برای برنامه تأثیر می‌گذارد، چون برنامه‌ها در این مدت نمی‌توانند به داده دسترسی پیدا کنند.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای انجام Move Data، ابتدا باید دادهٔ منتقل‌شونده و هر وابستگی به آن را شناسایی کنید. آیا ردیف متناظر در جدول دیگر حذف شود، یا مقدار ستون در ردیف متناظر null/صفر شود، یا مقدار متناظر دست‌نخورده بماند؟ آیا ردیف منتقل می‌شود یا فقط برخی ستون(ها)؟

دوم، مقصد داده را شناسایی کنید. داده به کجا منتقل می‌شود؟ به جدول یا جداول دیگر؟ آیا داده در حین انتقال تبدیل می‌شود؟ وقتی ردیف‌ها را منتقل می‌کنید، باید اطمینان حاصل کنید همهٔ جداول وابسته با ارجاع کلید خارجی به جدولی که داده از آن منتقل می‌شود، اکنون به جدولی که مقصد انتقال است ارجاع دهند.

مکانیک مهاجرت داده (Data-Migration Mechanics)

وقتی مقدار کمی داده باید منتقل شود، احتمالاً اسکریپت SQL ساده‌ای که دادهٔ منبع را در محل هدف درج و سپس منبع را حذف می‌کند کافی است. برای حجم زیاد داده، باید رویکرد پیچیده‌تری بگیرید چون زمان انتقال زیاد است. ممکن است نیاز به export دادهٔ منبع و import در محل هدف داشته باشید. همچنین می‌توانید از ابزارهای پایگاه داده مانند SQLLDR اوراکل یا bulk loader استفاده کنید.

شکل ۷.۱۲ نشان می‌دهد چگونه داده در ستون Customer.Status را به جدول CustomerStatusHistory منتقل کردیم تا بتوانیم وضعیت مشتری را در طول زمان ردیابی کنیم. کد زیر DML مربوط به انتقال داده از ستون Customer.Status به CustomerStatusHistory را نشان می‌دهد. ابتدا داده را از جدول Customer در CustomerStatusHistory درج می‌کنیم، سپس ستون Customer.Status را به NULL به‌روز می‌کنیم:

شکل ۷.۱۲. انتقال دادهٔ وضعیت از Customer به CustomerStatusHistory.

sql
INSERT INTO CustomerStatusHistory (CustomerId,Status,EffectiveDate)
SELECT CustomerId,Status,Sysdate FROM Customer;

UPDATE Customer SET Status = NULL;

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی Move Data در نتیجهٔ بازآرایی پایگاه دادهٔ دیگری اعمال می‌شود، برنامه‌ها باید از قبل برای بازتاب آن بازآرایی به‌روز شده باشند. اما وقتی Move Data برای پشتیبانی از سازمان‌دهی مجدد داده در طرحوارهٔ موجود اعمال می‌شود، ممکن است کد برنامهٔ بیرونی نیاز به به‌روزرسانی داشته باشد. مثلاً بندهای SELECT ممکن است نیاز به بازنگری داشته باشند تا دادهٔ منتقل‌شده را از منبع(های) جدید بخوانند. قطعهٔ کد زیر نحوهٔ تغییر متد getCustomerStatus() را نشان می‌دهد:

java
// Before code
public String getCustomerStatus(Customer customer) {
  return customer.getStatus();
}

// After code
public String getCustomerStatus(Customer customer) throws SQLException {
  stmt.prepare(
    "SELECT Status "+
    "FROM CustomerStatusHistory " +
    "WHERE " +
    "CustomerId = ? "+
    "AND EffectiveDate < TRUNC(SYSDATE) "+
    "ORDER BY EffectiveDate DESC");
    stmt.setLong(1,customer.getCustomerId);
  ResultSet rs = stmt.execute();
  if (rs.next()) {
    return rs.getString("Status");
  }
  throw new CustomerStatusNotFoundInHistoryException();
}

Replace Type Code With Property Flags — جایگزینی کد نوع با پرچم‌های ویژگی

ستون کد را با پرچم‌های ویژگی فردی — معمولاً به‌صورت ستون‌های Boolean — در همان جدول جایگزین کنید.

توجه کنید برخی محصولات پایگاه داده ستون‌های Boolean بومی پشتیبانی می‌کنند. در محصولات دیگر می‌توانید از انواع دادهٔ جایگزین برای مقادیر Boolean استفاده کنید (مثلاً NUMBER(1) در Oracle، که مقدار ۱ به معنای TRUE و ۰ به معنای FALSE است).

انگیزه (Motivation)

بازآرایی Replace Type Code With Property Flags برای موارد زیر استفاده می‌شود:

  • نرمال‌سازی معکوس برای عملکرد. وقتی می‌خواهید عملکرد را بهبود دهید با داشتن ستونی برای هر نمونه از کد نوع. مثلاً ستون Address.Type مقادیر Home و Business دارد؛ با ستون‌های پرچم ویژگی isHome و isBusiness جایگزین می‌شود. این جستجوی کارآمدتری امکان‌پذیر می‌کند چون مقایسهٔ دو مقدار Boolean سریع‌تر از مقایسهٔ دو مقدار رشته است.
  • ساده‌سازی انتخاب‌ها. ستون‌های کد نوع وقتی انواع متقابلاً انحصاری هستند خوب کار می‌کنند؛ اما وقتی نیستند، جستجو مشکل‌ساز می‌شود. مثلاً فرض کنید آدرس می‌تواند همزمان منزل و محل کار باشد. با رویکرد کد نوع به مقادیر Home، Business و HomeAndBusiness نیاز دارید. برای فهرست همهٔ آدرس‌های تجاری باید عبارتی مانند SELECT * FROM Address WHERE Type = "Business" OR Type = "HomeAndBusiness" اجرا کنید. با رویکرد پرچم ویژگی، عبارت SELECT * FROM Address WHERE isBusiness = TRUE ساده است و اگر نوع آدرس جدیدی مانند خانهٔ تعطیلات اضافه شود نیازی به تغییر ندارد.
  • جداسازی برنامه‌ها از مقادیر کد نوع. وقتی چند برنامه از Account.AccountType شکل ۷.۱۳ استفاده می‌کنند، تغییر مقادیر دشوار می‌شود چون بیشتر برنامه‌ها مقادیر را هاردکد کرده‌اند. وقتی این کدهای نوع با ستون‌های پرچم ویژگی جایگزین می‌شوند، برنامه‌ها فقط باید مقادیر استاندارد TRUE یا FALSE را بررسی کنند. با ستون کد نوع، برنامه‌ها به نام ستون و مقادیر درون آن وابسته‌اند؛ با ستون‌های پرچم ویژگی، فقط به نام ستون‌ها وابسته‌اند.

شکل ۷.۱۳. جایگزینی ستون کد AccountType با پرچم‌های ویژگی.

معاوضه‌های بالقوه (Potential Tradeoffs)

هر بار که می‌خواهید نوع مقدار جدیدی اضافه کنید، باید ساختار جدول را تغییر دهید. مثلاً برای افزودن نوع حساب money market باید ستون isMoneyMarket به جدول Account اضافه کنید. پس از مدتی این مطلوب نیست چون جداول با ستون‌های زیاد سخت‌تر فهمیده می‌شوند. نتیجهٔ join با این جدول هر بار ستون نوع جدید اضافه می‌شود بزرگ‌تر می‌شود. با این حال، افزودن ستون مستقل از بقیهٔ ستون‌ها بسیار آسان است. با رویکرد ستون کد نوع، ستون به همهٔ برنامه‌هایی که به آن دسترسی دارند وابسته است.

مکانیک به‌روزرسانی طرحواره (Schema Update Mechanics)

برای Replace Type Code With Property Flags در جدول پایگاه داده باید موارد زیر را انجام دهید:

  1. کد نوع جایگزین‌شونده را شناسایی کنید. ستون کد نوعی که باید جایگزین شود را پیدا کنید — در مثال ما Account.AccountType است. همچنین باید نمونه‌های کد نوع را پیدا کنید — مثلاً برای جایگزینی AddressType باید همهٔ انواع آدرس را پیدا کرده و همهٔ نمونه‌های AddressType را جایگزین کنید.
  2. ستون‌های پرچم ویژگی را معرفی کنید. پس از شناسایی نمونه‌های کدهای نوع، باید به همان تعداد ستون به جدول با Introduce New Column (صفحهٔ ۳۰۱) اضافه کنید. در شکل ۷.۱۳، ستون‌های HasChecks، HasFinancialAdviceRights، HasPassbook و IsRetailCustomer را به Account اضافه می‌کنیم.
  3. ستون کد نوع را حذف کنید. پس از تبدیل همهٔ کدهای نوع به پرچم‌های ویژگی، باید ستون کد نوع را با Drop Column (صفحهٔ ۷۲) حذف کنید.

شکل ۷.۱۳ نشان می‌دهد چگونه ستون Account.AccountType را به ستون‌های پرچم نوع برای هر نمونه از دادهٔ AccountType تغییر می‌دهیم. در مثال، چهار نوع حساب مختلف داریم که به ستون‌های پرچم HasChecks، HasFinancialAdviceRights، HasPassbook و isRetailCustomer تبدیل می‌شوند. SQL زیر نحوهٔ افزودن چهار ستون پرچم نوع به جدول Account، trigger همگام‌سازی در دورهٔ گذار، کد حذف ستون اصلی، و trigger پس از پایان دورهٔ گذار را نشان می‌دهد:

sql
ALTER TABLE Account ADD COLUMN HasChecks Boolean;
COMMENT ON Account.HasChecks "Replaces AccountType of Checks"

ALTER TABLE Account ADD COLUMN HasFinancialAdviceRights Boolean;
COMMENT ON Account.HasFinancialAdviceRights "Replaces AccountType of FinancialAdviceRights"

ALTER TABLE Account ADD COLUMN HasPassbook Boolean;
COMMENT ON Account.HasPassbook "Replaces AccountType of Passbook"

ALTER TABLE Account ADD COLUMN isRetailCustomer Boolean;
COMMENT ON Account.isRetailCustomer "Replaces AccountType of RetailCustomer"

CREATE OR REPLACE TRIGGER SynchronizeAccountTypeColumns
BEFORE INSERT OR UPDATE
ON ACCOUNT
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
BEGIN
   IF (:NEW.HasChecks IS NULL AND :NEW.AccountType = 'CHECKS') THEN
     :NEW.HasChecks := TRUE;
   END IF;
   IF (:NEW.HasFinancialAdviceRights IS NULL AND :NEW.AccountType =
  'FINANCIALADVICERIGHTS') THEN
     :NEW.HasFinancialAdviceRights := TRUE;
   END IF;
   IF (:NEW.HasPassbook IS NULL AND :NEW.AccountType = 'PASSBOOK') THEN
     NEW.HasPassbook := TRUE;
   END IF;
   IF (:NEW.isRetailCustomer IS NULL AND
:NEW.AccountType ='RETAILCUSTOMER') THEN
     :NEW.isRetailCustomer := TRUE;
   END IF;
END;
/

-- On June 14 2007
ALTER TABLE Account DROP COLUMN AccountType;

DROP TRIGGER SynchronizeAccountTypeColumns;

مکانیک مهاجرت داده (Data-Migration Mechanics)

باید اسکریپت‌های مهاجرت داده بنویسید تا پرچم‌های ویژگی را بر اساس مقدار کد نوع به‌روز کنید — می‌توانید از Update Data (صفحهٔ ۳۱۰) استفاده کنید. در فاز گذار، وقتی هم ستون کد نوع و هم ستون‌های پرچم نوع وجود دارند، باید ستون‌ها همگام نگه داشته شوند تا برنامه‌هایی که از داده استفاده می‌کنند اطلاعات سازگار بگیرند. این کار با triggerهای پایگاه داده امکان‌پذیر است. SQL زیر نحوهٔ به‌روزرسانی دادهٔ موجود در جدول Account را نشان می‌دهد (برای جزئیات Update Data را ببینید):

sql
UPDATE Account SET HasChecks = TRUE
  WHERE AccountType = 'CHECKS';
UPDATE Account SET HasChecks = FALSE
  WHERE HasChecks != TRUE;

UPDATE Account SET HasFinancialAdviceRights = TRUE
  WHERE AccountType = 'FINANCIALADVICERIGHTS';
UPDATE Account SET HasFinancialAdviceRights = FALSE
  WHERE HasFinancialAdviceRights != TRUE;

UPDATE Account SET HasPassbook = TRUE
  WHERE Accounttype = 'PASSBOOK';
UPDATE Account SET HasPassbook = FALSE
  WHERE HasPassbook != TRUE;
UPDATE Account SET isRetailCustomer = TRUE
  WHERE Accounttype ='RETAILCUSTOMER';
UPDATE Account SET isRetailCustomer = FALSE
  WHERE isRetailCustomer!= TRUE;

مکانیک به‌روزرسانی برنامهٔ دسترسی (Access Program Update Mechanics)

وقتی Replace Type Code With Property Flags اعمال می‌شود، باید برنامه‌های بیرونی را در دو روش به‌روز کنید. اول، کد SQL (یا متادیتا) که داده را از این ستون ذخیره، حذف و بازیابی می‌کند باید با ستون‌های پرچم نوع فردی کار کند نه AccountType. مثلاً وقتی SELECT * FROM Account WHERE AccountType = 'XXXX' داریم، SQL به SELECT * FROM Account WHERE isXXXX = TRUE تغییر می‌کند. به‌طور مشابه، باید کد برنامه را برای به‌روزرسانی ستون‌های پرچم نوع به‌جای AccountType در عملیات insert یا update تغییر دهید.

دوم، ممکن است نیاز باشد کد برنامه‌ای که با ستون کار می‌کند به‌روز شود. مثلاً کد مقایسه‌ای مانند Customer.AddressType = 'Home' باید برای کار با isHome به‌روز شود.

کد زیر نحوهٔ تغییر کلاس Account وقتی کد نوع حساب را با پرچم‌های ویژگی جایگزین می‌کنید را نشان می‌دهد:

java
// Before code
public class Account {

private Long accountID;
private BigDecimal balance;
private String accountType;
private Boolean FALSE = Boolean.FALSE;
private Boolean TRUE = Boolean.TRUE;

public Long getAccountID() {
  return accountID;
}

public BigDecimal getBalance() {
  return balance;
}

public Boolean HasChecks() {
  return accountType.equals("CHECKS");
}

public Boolean HasFinancialAdviceRights() {
  return accountType.equals("FINANCIALADVICERIGHTS");
}

public Boolean HasPassBook() {
  return accountType.equals("PASSBOOK");
}
public Boolean IsRetailCustomer() {
  return accountType.equals("RETAILCUSTOMER");
}
}

// After code
public class Account {

private Long accountID;
private BigDecimal balance;
private Boolean HasChecks;
private Boolean HasFinancialAdviceRights;
private Boolean HasPassBook;
private Boolean IsRetailCustomer;

public Long getAccountID() {
  return accountID;
}

public BigDecimal getBalance() {
  return balance;
}

public Boolean HasChecks() {
  return HasChecks;
}

public Boolean HasFinancialAdviceRights() {
  return HasFinancialAdviceRights;
}

public Boolean HasPassBook() {
return HasPassBook;
}

public Boolean IsRetailCustomer() {
  return IsRetailCustomer;
}
}