2012/06/12

Ràng buộc FOREIGN KEY trong SQL Server

Các bảng trong một cơ sở dữ liệu có mối quan hệ với nhau. Những mối quan hệ này biểu diễn cho sự quan hệ giữa các đối tượng trong thế giới thực. Về mặt dữ liệu, những mối quan hệ được đảm bảo thông qua việc đòi hỏi sự có mặt của một giá trị dữ liệu trong bảng này phải phụ thuộc vào sự tồn tại của giá trị dữ liệu đó ở trong một bảng khác.


Ràng buộc FOREIGN KEY được sử dụng trong định nghĩa bảng dữ liệu nhằm tạo nên mối quan hệ giữa các bảng trong một cơ sở dữ liệu. Một hay một tập các cột trong một bảng được gọi là khoá ngoại, tức là có ràng buộc FOREIGN KEY, nếu giá trị của nó được xác định từ khoá chính (PRIMARY KEY) hoặc khoá phụ (UNIQUE) của một bảng dữ liệu khác.

Ràng buộc FOREIGN KEY được định nghĩa theo cú pháp dưới đây:


[CONSTRAINT tên_ràng_buộc]
FOREIGN KEY [(danh_sách_cột)]
REFERENCES tên_bảng_tham_chiếu(danh_sách_cột_tham_chiếu)
[ON DELETE CASCADE | NO ACTION | SET NULL | SET DEFAULT]
[ON UPDATE CASCADE | NO ACTION | SET NULL | SET DEFAULT]

Việc định nghĩa một ràng buộc FOREIGN KEY bao gồm các yếu tố sau:

• Tên cột hoặc danh sách cột của bảng được định nghĩa tham gia vào khoá ngoài.
• Tên của bảng được tham chiếu bởi khoá ngoài và danh sách các cột được tham chiếu đến trong bảng tham chiếu.
• Cách thức xử lý đối với các bản ghi trong bảng được định nghĩa trong trường hợp các bản ghi được tham chiếu trong bảng tham chiếu bị xoá (ON DELETE) hay cập nhật (ON UPDATE). SQL chuẩn đưa ra 4 cách xử lý:
  • CASCADE: Tự động xoá (cập nhật) nếu bản ghi được tham chiếu bị xoá (cập nhật).
  • NO ACTION: (Mặc định) Nếu bản ghi trong bảng tham chiếu đang được tham chiếu bởi một bản ghi bất kỳ trong bảng được định nghĩa thì bàn ghi đó không được phép xoá hoặc cập nhật (đối với cột được tham chiếu).
  • SET NULL: Cập nhật lại khoá ngoài của bản ghi thành giá trị NULL (nếu cột cho phép nhận giá trị NULL).


  • SET DEFAULT: Cập nhật lại khoá ngoài của bản ghi nhận giá trị mặc định (nếu cột có qui định giá trị mặc định).




  • Ví dụ: Câu lệnh dưới đây định nghĩa bảng DIEMTHI với hai khoá ngoài trên cột MASV và cột MAMONHOC (giả sử hai bảng SINHVIEN và MONHOC đã được định nghĩa)
    CREATE TABLE diemthi
    (
    mamonhoc NVARCHAR(10) NOT NULL ,
    masv NVARCHAR(10) NOT NULL ,
    diemlan1 NUMERIC(4, 2),
    diemlan2 NUMERIC(4, 2),
    CONSTRAINT pk_diemthi PRIMARY KEY(mamonhoc,masv),
    CONSTRAINT fk_diemthi_mamonhoc
    FOREIGN KEY(mamonhoc)
    REFERENCES monhoc(mamonhoc)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    CONSTRAINT fk_diemthi_masv
    FOREIGN KEY(masv)
    REFERENCES sinhvien(masv)
    ON DELETE CASCADE
    ON UPDATE CASCADE
    )

    Lưu ý:
    • Cột được tham chiếu trong bảng tham chiếu phải là khoá chính (hoặc là khoá phụ).
    • Cột được tham chiếu phải có cùng kiểu dữ liệu và độ dài với cột tương ứng trong khóa ngoài.
    • Bảng tham chiếu phải được định nghĩa trước. Do đó, nếu các bảng có mối quan hệ vòng, ta có thể không thể định nghĩa ràng buộc FOREIGN KEY ngay trong câu lệnh CREATE TABLE mà phải định nghĩa thông qua lệnh ALTER TABLE.

    Khi nào và tại sao phải dùng Index trong MySQL

    Một ngày kia bạn nhận ra rằng website của bạn chạy chậm đi, có thể là do đường truyền nhưng còn 1 nguyên nhân khác, đó là máy chủ server tính toán quá nhiều dẫn đến kết quả đưa ra. Đây là điều thường thấy ở những website về Diễn Đàn (Forum), Tin Tức (Portal) và Thương mại điện tử (Ecommerce). Khi số lượng thành viên, số lượng bài viết tăng lên, đồng nghĩa với việc Database khi truy vấn (query) 1 yêu cầu phải duyệt qua tất cả các dữ liệu hiện có để tìm ra dữ liệu thích hợp. Cũng giống như 1 quyển sách. Nếu sách là mỏng, bạn dễ dàng tìm ra thông tin mình cần. Nhưng khi sách dầy lên, thời gian tìm kiếm của bạn sẽ tăng đáng kể.

    Việc Database quá tải còn dẫn đến nhiều thiệt hại khác, các hàng đợi (Queuie) dài ra, file logs lớn lên chiếm hết không gian đĩa và user khi kết nối sẽ bị từ chối. Rõ ràng là câu báo lỗi “Too many connections” không phải là hiếm gặp trong các website trên Internet. Những lỗi trên thông thường bắt nguồn từ khâu định nghĩa Database (define) hay không sử dụng Indexes. Khắc phục những thiếu sót trên, Database của bạn sẽ “nhẹ nhàng” và nhanh chóng đáng kể. Hãy xem xét ví dụ sau:

    01CREATE TABLE tblUsers (
    02user_id int(11) NOT NULL,
    03user_name vachar(25),
    04user_pwd vachar(50),
    05user_firstname varchar(40),
    06user_surname varchar(40),
    07user_address text,
    08user_tel varchar(25),
    09user_goal int(11),
    10);
    Và để tìm thông tin Điểm của Nguyễn Văn A (mã số 120956), bạn sẽ query như sau:
    1SELECT user_goal FROM tblUsers WHERE user_id = ‘120956′;
    MySQL biết rằng phải tìm ở table tblUsers nhưng nó sẽ không biết bắt đầu từ đâu. Thậm chí nó cũng không biết trước rằng có bao nhiêu kết quả . Do đó nó sẽ duyệt qua tất cả danh sách (vd Hơn 300.000 người) để tìm thông tin về Nguyễn Văn A.
    Index là 1 file riêng biệt được lưu trữ ở máy chủ và chỉ chứa những Fields mà bạn muốn nó chứa. Nếu bạn tạo 1 Index cho Field user_id (mã số người dùng), MySQL sẽ dễ dàng tìm ra được mã số 1 cách nhanh chóng. Trở lại ví dụ quyển sách, khi cần tìm 1 thông tin, ta thường lật ngay tới phần “Mục Lục” và tìm từ đó để tăng tốc độ tìm. Và việc tạo ra Index này sẽ làm bạn thấy Database của bạn chạy nhanh 1 cách khác thường.

    Nhưng trước khi sửa lại cấu trúc của table ở trên, tôi sẽ hướng dẫn bạn 1 chút về cách theo dõi kết quả “Tăng tốc MySQL” mà bạn đang làm. Hãy sử dụng lệnh EXPLAIN

    Cú pháp: EXPLAIN Query;

    Bằng lệnh này bạn sẽ nhận ra được với câu Query của bạn, điều gì đang xảy ra và kiểu kết hợp (Join) nào đang diễn ra bên trong.
    Xem ví dụ sau:
    1mysql>EXPLAIN SELECT user_id,user_firstname,user_surname FROM tblUsers WHERE user_id= ‘120956‘;
    +———-+——+—————+——+———+——+————+————+
    | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +———-+——+—————+——+———+——+————+————+
    | tblUsers | ALL | NULL | NULL | NULL | NULL | 86792| where used |
    +———-+——+—————+——+———+——+————+————+

    Giải thích:
    - table : Table nào đang liên quan đến output data
    - type : Đây là thông tin quan trọng, nó cho chúng ta biết kiểu query nào nó đang sử dụng. Mức độ từ tốt nhất đến chậm nhất như sau: system, const, eq_ref, ref, range, index, all
    - possible_keys : Đưa ra những Index có thể sử dụng để query
    - key : và Index nào đang được sử dụng
    - key_len : Chiều dài của từng mục trong Index
    - ref : Cột nào đang sử dụng
    - rows : Số hàng (rows) mà MySQL dự đoán phải tìm
    - extra : Thông tin phụ, thật tệ nếu tại cột này là “using temporary” hay “using filesort”

    Wow, nhìn lại câu query của chúng ta mới thật khủng khiếp. Không có Possible_keys nào được sử dụng, MySQL phải duyệt qua 86792 bản ghi mới tìm ra cái ta cần (Hãy tưởng tượng 1 Forum sẽ có đến hơn 500.000 bản ghi).

    Bây giờ chúng ta sẽ thêm Index vào và query lại
    1mysql>ALTER TABLE tblUsers ADD INDEX idx_userid(user_id);
    2mysql>EXPLAIN SELECT user_id,user_firstname,user_surname FROM tblUsers WHERE user_id= ‘120956‘;
    +———-+——-+—————+———+———+——-+——+——-+
    | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +———-+——-+—————+———+———+——-+——+——-+
    | tblUsers | const | PRIMARY | PRIMARY | 10 | const | 1 | |
    +———-+——-+—————+———+———+——-+——+——-+

    Tốt hơn nhiều rồi, kiểu TYPE = Const có nghĩa rằng MYSQL hiểu ra chỉ có 1 hàng đúng với ý ta, và thể hiện qua cột Rows = 1, kiểu key= PRIMARY được sử dụng và chiều dài key_len là 10.Chỉ tìm 1 hàng tất nhiên rằng tốt hơn nhiều so với tìm 86792 hàng
    Vậy câu hỏi đặt ra là, nếu tôi muốn thêm Index cho những cột mà có thể có nhiều hơn 1 kết quả khi query thì sao?

    Vẫn add index như bình thường,giả sử bạn cần tìm những người có họ là Nguyễn Văn, tên là A
    1mysql>ALTER TABLE tblUsers ADD INDEX idxSFname (user_surname, user_firstname);
    2mysql>EXPLAIN SELECT user_goal FROM tblUsers WHERE user_surname=’Nguyễn Văn‘ AND user_firstname=”A“;
    +——–+——+——————-+———+———+——-+——+———–+
    | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +——–+——+——————-+———+———+——-+——+———–+
    |tblUsers| ref | user_surname,user_firstname | idxSFname | 41 | const | 1 |where used |
    +——–+——+——————-+———+———+——-+——+———–+

    Tuy nhiên, nếu chỉ cần user_firstname
    1mysql>EXPLAIN SELECT user_goal FROM tblUsers WHERE user_firstname=’Name’;
    +———-+——+—————+——+———+——+————+————+
    | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +———-+——+—————+——+———+——+————+————+
    | tblUsers | ALL | NULL | NULL | NULL | NULL | 86792 | where used |
    +———-+——+—————+——+———+——+————+————+

    thì MySQL sẽ tìm hết vì không hề có Index cho user_firstname mà chỉ có Index cho (user_surname, user_firstname).
    Khi nào thì cần Add Index ? Bất cứ khi nào bạn thay đổi Table bạn đều cần Add Index lại, giống như khi bạn thay đổi nội dung quyển sách, bạn cần phải làm lại mục lục. Vậy hãy cân nhắc, nếu Database của bạn sử dụng INSERT hay UPDATE nhiều hơn là SELECT thì Index chỉ làm chậm thêm mà thôi.

    Có thể nhanh hơn nữa không ? Câu trả lời là Có! Bạn không cần phải làm Index cho cả Field mà chỉ cần 1 phần. Giống như chi tiết Mục Lục của sách mà quá dài cũng làm bạn khá vất vả, do đó họ chỉ trích dẫn 1 tựa đề. Quay lại với table của chúng ta, user_surname và user_firstname chỉ maximum là 40 chars, nếu chúng ta index nó, chúng ta tạo ra mỗi record đến 80 chars . Có thể tiết kiệm bằng cách sau
    1mysql>ALTER TABLE tblUsers ADD INDEX(user_surname(20),user_firstname(20));
    Bây giờ thì bạn tiết kiệm được đến 50% mà vẫn đảm bảo được tốc độ rồi đó (trừ phi bạn làm Index quá ngắn). Có thể bạn nói đĩa cứng server tôi “vô tư” nhưng hãy nhớ rằng “Nhỏ hơn là nhanh hơn”.

    ĐIỀU KÌ DIỆU VỚI OPTIMIZE VÀ ANALYZE
    “Ma thuật” của MySQL là biết cách chọn khoá (key) nào để query(nếu có). Quá trình này gọi là “query optimizer”, nó sẽ “liếc” qua những Index đang có để quyết định sẽ dùng Index nào để tìm. Hãy tưởng tượng bạn đang tìm 1 dĩa CD của “Maria Carrey” có tên là “I Love You”, có nghĩa là có 2 Indexes ở đây, 1 cho tên tác giả và 1 cho tên CD. Bạn nhận thấy rằng danh mục có 20.000 tên tác giả và 400.000 tên Album. Một cách đơn giản,bạn sẽ tìm theo tên tác giả. Khi có được, bạn lại thấy rằng “Maria Carrey” có 50 CDs và CD “I Love You” bắt đầu bằng chữ I. Đơn giản và dễ dàng tìm ra cái mình muốn phải không ? MySQL cũng vậy nhưng …bạn phải chỉ cho nó bằng cách:

    ANALYZE TABLE tablename;
    Những lệnh DELETE và UPDATE để lại rất nhiều những khoảng trống (gaps) vô nghĩa cho table (Đặc biệt là khi bạn dùng kiểu varchar hay text/blob). Điều đó có nghĩa rằng MySQL cũng phải đọc và phân tích những thứ vô nghĩa đó khi query. Điều này được khắc phục khi bạn chạy

    OPTIMIZE tablename;
    Do đó 2 câu lệnh trên bạn nên chạy 1 cách thường xuyên để bảo đảm tối ưu hoá Database của mình.


    (Nguồn simple.vn)

    Một số đặc điểm của InnoDB và MyISAM trong cơ sở dữ liệu MySQL

    Sau 1 thời gian sử dụng Mysql nhưng mình cũng không để ý lắm đến khả năng lưu trữ cũng như hiệu xuất của nó cho lắm. Hôm nay có dự án khá lớn yêu cầu CSDL to nên mình mới tìm hiểu sâu hơn và thấy được nhiều điều rất thú vị.

    MySql phiên bản 4.x không thể so với MSSQL vì phiên bản này thiếu khá nhiều tính năng quan trọng để được xếp loại là một CSDL hạng nặng (ví dụ như thiếu ứng dụng View, StoreProc, Triggers...). Để công bằng nên thử so sánh MySql 5.x và MSSQL 2005.


    1. Tính năng:
    MySql hơn MSSQL ở tính năng cung cấp nhiều loại storage engine. Tuy nhiên tính về mặt integration chặt chẽ cho trọn bộ hệ thống và công cụ phát triển software thì MSSQL integrate tốt hơn MySql, đặc biệt ở mảng .NET. MSSQL còn hỗ trợ XML trực tiếp trong DB, trong khi đó thì MySQL chưa làm được chuyện này. Các chức năng cần thiết như View, StoreProcedure, Trigger thì cả hai tương đương nhau.

    2. Hiệu suất:
    Tính về mặt hiệu suất, có rất nhiều thử nghiệm và tường trình cho rằng MSSQL perform kém hơn MySQL nhiều mặt. MSSQL đòi hỏi tài nguyên rất lớn (CPU mạnh, nhiều RAM...), nếu không nó rất ì ạch. MySQL không đòi hỏi nhiều như MSSQL. MySQL có thể chạy trên các UNIX highend system và perform tốt hơn MSSQL trên Windows highend server trong nhiều trường hợp.

    3. Bảo mật:
    MSSQL qua mặt MySQL về tính bảo mật ở column level. MySQL chỉ có thể set access đến row level là hết. Hệ thống xác thực người dùng của MSSQL cũng qua mặt MySQL. Tuy vậy, trên bình diện "để hở cổng" thì MSSQL dễ bị exploit hơn MySQL bởi MSSQL tích hợp quá chặt vào hệ điều hành. Lỗi của hệ điều hành hoặc một bộ phận nào đó dẫn đến việc nhân nhượng MSSQL nhanh chóng hơn MySQL.

    4. Khả năng nhân bản (replication):
    Cả hai đều có khả năng replicate hoặc cung cấp phương tiện để làm việc này. Tuy nhiên, MySQL nhanh hơn và ít sự cố hơn MSSQL vì tất cả các SQL statements dùng để thay đổi, cập nhật dữ liệu được lưu giữ trong binary log. MSSQL cung cấp nhiều phương pháp replicate cao cấp hơn, chi tiết hơn bởi thế nó phức tạp hơn và chậm hơn.

    5. Khả năng phục hồi (recovery):
    Nếu MySQL chạy thuần túy với MyISAM storage engine thì khả năng phục hồi (sau khi bị crash) không cách gì so sánh với MSSQL. Tuy nhiên nếu MySQL chạy với Innodb engine thì khả năng phục hồi không thua kém gì MSSQL. Ngoài ra, khả năng phục hồi còn được xét ở biên độ liên quan đến khả năng nhân bản (replication) ở trên. Bởi vì MSSQL cung cấp nhiều phương thức nhân bản nên việc phục hồi dữ liệu trên MSSQL dễ dàng hơn.


    Một số đặc điểm của InnoDB và MyISAM trong cơ sở dữ liệu MySQL:

    - InnoDB hỗ trợ relationship (data integrity and foreign key constraints) còn MyISAM thì ko: Đa phần các open source đều không coi trọng việc này nhưng nếu ứng dụng của bạn bắt buộc phải dùng foreign key constraints thì InnoDB là lựa chọn của bạn.

    - InnoDB hỗ trợ transactions còn MyISAM thì không: Nếu hệ thống của bạn dùng trong các ứng dụng ngân hàng hoặc phải thực hiện việc giao dịch thì chắc chắn là MyISAM sẽ bị loại.

    + Khi nào cần dùng Transaction ?
    Khi ta muốn bảo đảm sự toàn vẹn của dữ liệu (không tạo ra các record mồ côi hoặc chứa thông tin sai lệch).

    + DùngTransaction để làm gì?
    Để đảm bảo sự toàn vẹn của dữ liệu.

    + Lợi ích của nó ?
    Lợi ích là đảm bảo sự toàn vẹn của dữ liệu.

    Ví dụ như trong ngân hàng bạn còn $150, bạn lên internet, vào 2 tragn wweb cùng 1 lúc, mua 2 món hàng cùng lúc, một món trị giá $50 và 1 món trị giá $20.
    Vậy, nếu đúng thì tài khoản của bạn phải còn lại $150 - $50-$20 = $80.

    Tuy nhiên, thử tưởng tượng như sau:
    - Đầu tiên ngân hàng nhận được yêu cầu mua hàng từ trang web 1, nó đọc tài khoản của bạn ra giá trị $150.
    - Ngay lúc đó ngân hàng cũng nhận được yêu cầu từ trang web 2, nó đọc tài khoản của bạn, ra giá trị $150.
    - Sau đó, vụ mua bán thứ nhất kết thúc, ngân hàng ghi lại vào tải khoản của bạn là $150-$50 = $100
    - Lúc này vụ mua bán thứ 2 kết thúc và ngân hàng ghi lại vào tải khoản của bạn $150-$20 = $130
    => vậy là cuối cùng bạn còn $130 (sai) thay vì $80 (đúng).
    Do vậy transaction được dùng để tránh những trường hợp tương tự như trên xảy ra.

    - InnoDB thiên về row-level locking còn MyISAM thiên về table locking: Tức là khi hệ thống của bạn phải thực hiện nhiều các thao tác insert/update thì InnoDB là tốt hơn, còn nếu hệ thống của bạn thực hiện các thao tác select là chủ yếu thì dùng MyISAM là lựu chọn tốt hơn.

    - MyISAM hỗ trợ full-text searches còn InnoDB thì không: Đây rõ ràng là một điểm yếu của InnoDB so với MyISAM, và dĩ nhiên là trong hệ thống có dùng full-text searches thì phải loại InnoDB đầu nước.

    - Tốc độ của MyISAM cao hơn InnoDB: Khi hệ thống của bạn đòi hỏi performance cao thì MyISAM là lựa chọn tốt hơn.

    - Cuối cùng nếu bạn là người mới làm về MySQL (cũng như DB nói chung) thì bạn nên dùng MyISAM vì rằng nó đơn giản hơn InnoDB.


    6. Phí tổn:

    MySQL bản community không hề tốn tiền để mua nhưng bạn phải tự xắn tay áo lên. Tuy nhiên cài đặt, sử dụng và tối ưu MySQL không khó vì tài liệu của nó rất đầy đủ, thông tin về MySQL tràn ngập trên web. So với MSSQL, bạn phải trả gần một ngàn rưỡi đô cho 1 license MSSQL standard và khi cần support, bạn phải trả thêm tiền support (tùy case).

    MS vẫn cung cấp bản MSSQL không thu phí dành cho mục đích development. Tài liệu về MSSQL cũng rất nhiều trên mạng. Bản MySQL enterprise thì phải trả tiền (khoảng 400 đô) nhưng bạn được support đầy đủ.

    And Mysql Vs Oracle ????

    + MySQL là phần mềm mã nguồn mở và nó được sự ủng hộ của cộng đồng hacker và phong trào mã nguồn mở.

    + Các công ty lớn có thể truy cập mã nguồn MySQL để tạo ra một bản khác dùng nội bộ công ty với mục đích phi thương mại mà không sợ vi phạm bản quyền.

    + Tốc độ MySQL khá nhanh, bảng của MySQL lên đến 8 gb đủ dùng cho các công ty không quá lớn. Tôi thì không nhớ nhưng MySQl hình như có thể xử lý được đến 12.000 connection đồng thời. Quá đủ dùng cho 80 % các công ty trên thế giới.

    + Các tính năng mạnh của MySQL đang được bổ sung dần qua từng phiên bản. Phiên bản 4.1 hiện tại không ít hơn tính năng là mấy so với SQL Server.

    + Chưa thấy ai chê về khả năng bảo mật của MySQL cả. Hồi SQL Server bị virus đập cho te tua thì MySQL hoàn toàn miễn dịch.

    + MySQL hoàn toàn miễn phí.
    + LAMP = Linux + Apache + MySQL + PHP đó là biểu tượng của công đồng mã nguồn mở
    + Số nhà phát triển dùng MySQL là rất lớn trên thế giới cho nên số các ứng dụng chạy trên MySQL là nhiều và cũng miễn phí

    Tóm lại là các chức năng sql chuẩn của Mysql chạy khá tốt, tốc độ nhanh, có thể lưu trữ được 8Gb cho mỗi bảng, 12.000 connection đồng thời (Quá đủ dùng cho 80 % các công ty trên thế giới) như vậy là có thừa đủ lý do để chọn Mysql rồi phải không nào.


    (Nguồn simple.vn)