Sunday, May 27, 2012

ความลึกลับของ Trigger ตอน3 (จบ)

วิธีการที่ถูกต้อง การแก้ไขโครงสร้างของตาราง จากวิธีการนำไปใช้และการออกแบบตารางจะเห็นได้ว่าเป็นวิธีการที่ผิด ปัญหานี้จำเป็นจะต้องใช้สองตารางดังนี้

SQL> create table primary_currency
2 (country varchar2(2) primary key,
3 currency varchar2(3)
4 )
5 /

Table created.

SQL> create table other_currencies
2 (country varchar2(2),
3 currency varchar2(3),
4 constraint other_currencies_pk
5 primary key (country,currency)
6 )
7 /

Table created.



และหากต้องการความสะดวกในการเรียกดูข้อมูลก็ควรจะเรียกดูผ่านวิวที่ Join เอาสองตารางเข้าด้วยกันแล้วแทน
 

โดยเราแยกตารางออกดังนี้ซึ่งเราได้บังคับใช้กฎไปจากการสร้าง Primary Key แล้วว่าแต่ละประเทศจะมีเงินสกุลหลักได้เพียงหนึ่งเดียวจาก Primary Key ในตาราง primary_currency อย่างไรก็ตามในโลกแห่งความเป็นจริง เราอาจจะต้องการระบุด้วยว่า "ทุกประเทศจะต้องมีอย่างเงินสกุลหลักอย่างน้อยหนึ่งสกุล" ด้วย ซึ่งเราสามารถที่จะกำหนดข้อบังคับนั้นได้โดยเพียงแค่เพิ่ม Contraint เข้าไปในตารางดังนี้


SQL> alter table other_currencies add
2 constraint must_have_at_least_one_primary
3 foreign key (country)
4 references primary_currency(country)
5 /

Table altered.

เสร็จ แล้วครับ คราวนี้เราจะได้ตารางที่มีการบังคับใช้กฎที่เราต้องการ โดยไม่ต้องเขียนโปรแกรม, ดูแลและเข้าใจง่าย Foreign Key ช่วยควบคุมไม่ให้สามารถเพิ่ม Currency สำหรับประเทศที่ยังไม่มี Primary Currency ได้ และที่สำคัญคือ ถูกต้อง

ปิดท้าย
ทริกเกอร์ ควรจะถูกใช้ด้วยความระมัดระวัง ถ้าเป็นการใช้เพื่อควบคุมความสอดคล้องกันของตาราง (Entity Integrity) จะต้องระวังให้มาก คิดให้ถี่ถ้วนว่าถ้ามีคนสอง หรือสามคนเข้ามาทำงานกับกลุ่มข้อมูลเดียวกัน ในเวลาเดียวกัน หรือไล่เลี่ยกัน อาจจะต้องลองทดสอบดูกับการรันในหลาย Session หรือถ้าใช้ทริกเกอร์ในการให้ค่าใด ๆ กับคอลัมน์ก็ต้องระวังกับเรื่องการดูแลในอนาคต เพราะไม่งั้นอาจจะเจอกับปัญหาที่ว่า "เอ๊ะ หนูไม่ยักรู้ว่ามันจะเป็นอย่างนี้" ถ้าพิจารณาเรื่องของการใช้งานพร้อม ๆ กัน (Concurrency) การทำงานที่ไม่ได้อยู่ในระดับ Transaction ของทริกเกอร์ และการดูแลรักษาที่ยุ่งยาก ทริกเกอร์ควรจะเป็นสิ่งสุดท้าย ไม่ใช่สิ่งแรกที่คุณจะทำ แต่ใช้ทริกเกอร์เมื่อคุณไม่สามารถจะหาวิธีการอื่นได้แล้ว

แล้ว ถ้ามีกฎอีกล่ะว่า "แต่ละประเทศจะมีอย่างมากที่สุดและอย่างน้อยที่สุด 1 สกุลเงินหลัก และสกุลเงินหลักจะต้องไม่เป็นเงินสกุลรอง (Other Currencies)" อันนี้จะซับซ้อนหน่อย ซึ่งมีคุณสมบัติเหมือน "Antiforeign Key" ซึ่งคุณสมบัติดังกล่าวไม่มีในระบบฐานข้อมูล เราสามารถจะใช้วิธีการอื่นในการบังคับใช้กฎนี้ได้ โดยทำให้เราสามารถแน่ใจได้ว่าถ้าเรา Join ตาราง PRIMARY_CURRENCY กับ OTHER_CURRENCIES โดยใช้คอลัมน์ COUNTRY and CURRENCY, เราจะได้ 0 เรคคอร์ดเสมอ ลองดู Listing ข้างล่างจะสร้าง Materialized View ซึ่งจะทำการ Join ตารางทั้งสองด้วยคอลัมน์ COUNTRY and CURRENCY โดยที่จะมีผลเป็น 0 เรคคอร์ดเสมอ

SQL> create materialized view log
2 on primary_currency with rowid
3 /
Materialized view log created.

SQL> create materialized view log
2 on other_currencies with rowid
3 /
Materialized view log created.

SQL> create materialized view primary_is_not_other
2 refresh fast
3 on commit
4 as
5 select a.rowid arid, b.rowid brid
6 from primary_currency a, other_currencies b
7 where a.country = b.country
8 and a.currency = b.currency
9 /
Materialized view created.

SQL> alter table primary_is_not_other
2 add constraint primary_curr_cannot_be_other
3 check (arid is null and brid is null)
4 /

Table altered.

ตอน นี้เราได้ Materialized View ที่จะทำการ Refresh เมื่อมีการ Commit คำสั่ง DML ที่ใช้กับตารางแม่ (primary_currency และ other_currencies) ซึ่งจะแสดง Error ถ้าผลการ Join ของสองตารางเป็น Null และรับประกันว่าผลจากการ Join จะได้เป็น 0 rows เสมอ ซึ่งจะไม่มีปัญหาในเรื่องการทำธุรกรรมพร้อม ๆ กัน และผลที่ได้จะถูกต้องเสมอ ซึ่งเป็นการให้ฐานข้อมูลเป็นตัวควบคุมกฎนี้ให้กับเรา

ขอจบบทความเรื่อง "ความลึกลับของทริกเกอร์" โดยบริบูรณ์ และขอให้สนุกกับการใช้ทริกเกอร์อย่างระมัดระวังนะครับ

No comments:

Post a Comment