2009-11-02 31 views
6

Tôi có 4 bảng (được chỉ định, hạng, bầu, tình trạng) mà tôi muốn tham chiếu chéo vào cột của một bảng (thành viên). Các giá trị của 4 bảng có độ nhạy thời gian dựa trên bảng lịch sử (members_history). Kết quả mong muốn là truy vấn sẽ xuất tất cả các thành viên và vị trí được chỉ định hiện tại hoặc vị trí, lớp và trạng thái được chọn hiện tại trong hàng thành viên và bao gồm thông tin bổ sung thu được từ các hàng bên ngoài.Phím ngoại động - Cách triển khai?

Vì vậy, thay vì chỉ trở về:

id, tên người dùng, mật khẩu, muối, name_first, name_last, date_join & date_leave;

Truy vấn sẽ trở lại

id, tên người dùng, mật khẩu, muối, name_prefix, name_first, name_last, hours_extra, date_join, date_leave, appointed, class, elected & status;

Bất cứ nơi nào cột được thêm vào không có giá trị hiện tại trong lịch sử, kết quả sẽ là NULL.

Bây giờ tôi nghĩ tôi có thể làm điều này với các truy vấn phụ, nhưng cho đến nay đã đập đầu tôi vào bàn phím. Tôi sẽ có một swing vào nó sau này, nhưng cho đến khi đó, bất cứ ai khác sẵn sàng cho nó một shot, hoặc cố gắng để chỉ cho tôi đi đúng hướng?

Cấu trúc của SQL của tôi (không ý định chơi chữ) bảng như sau:

CREATE TABLE IF NOT EXISTS `members` (
`id` mediumint(3) unsigned NOT NULL auto_increment COMMENT 'Members Unique Id', 
`username` varchar(32) collate utf8_bin NOT NULL COMMENT 'Mebers Username', 
`password` varchar(64) collate utf8_bin NOT NULL COMMENT 'Members Password Hash', 
`salt` varchar(32) collate utf8_bin NOT NULL COMMENT 'Members Password Salt', 
`name_first` varchar(32) collate utf8_bin NOT NULL COMMENT 'Members First Name', 
`name_last` varchar(32) collate utf8_bin NOT NULL COMMENT 'Members Last Name', 
`date_join` date NOT NULL COMMENT 'Members Join Date', 
`date_leave` date default NULL COMMENT 'Members Resgination Date (If Applicable)', 
PRIMARY KEY (`id`), 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Members id in this table = mid in other tables'; 

CREATE TABLE IF NOT EXISTS `members:apointed` (
`id` tinyint(3) unsigned NOT NULL auto_increment COMMENT 'Unique value', 
`name_prefix` varchar(8) collate utf8_bin NOT NULL COMMENT 'Prefix Added to Members Name', 
`hours_extra` decimal(4,2) NOT NULL COMMENT 'Hours Given as Bonus for Holding this Position.', 
`position` varchar(40) collate utf8_bin NOT NULL COMMENT 'Name of the Posisiton', 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Undefined within the SOP or By-Laws.'; 

CREATE TABLE IF NOT EXISTS `members:class` (
`id` tinyint(3) unsigned NOT NULL auto_increment COMMENT 'Unique Id', 
`class` varchar(8) collate utf8_bin NOT NULL COMMENT 'Unique Value', 
PRIMARY KEY (`id`), 
UNIQUE KEY `value` (`class`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Article I, Section 1 Subsection B: Classes of Membership'; 

CREATE TABLE IF NOT EXISTS `members:elected` (
`id` tinyint(3) unsigned NOT NULL auto_increment COMMENT 'Unique value', 
`name_prefix` varchar(8) collate utf8_bin NOT NULL COMMENT 'Prefix Added to Members Name', 
`hours_extra` decimal(4,2) NOT NULL COMMENT 'Hours Given as Bonus for Holding this Position.', 
`position` varchar(40) collate utf8_bin NOT NULL COMMENT 'Name of the Posisiton', 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Article II'; 

CREATE TABLE IF NOT EXISTS `members:status` (
`id` tinyint(3) unsigned NOT NULL auto_increment COMMENT 'Bit''s Place', 
`status` varchar(16) collate utf8_bin NOT NULL COMMENT 'Categorie''s Name', 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Article I, Section 1, Subsection A: Categories of Membership'; 

CREATE TABLE IF NOT EXISTS `members_history` (
`id` int(10) unsigned NOT NULL auto_increment COMMENT 'Unique Id', 
`mid` tinyint(3) unsigned NOT NULL COMMENT 'Members Unique Id.', 
`table` enum('class','elected','appointed','status') NOT NULL COMMENT 'Name of Table that was Edited.', 
`value` tinyint(3) unsigned NOT NULL COMMENT 'Value', 
`start` date NOT NULL COMMENT 'Value''s Effect Date', 
`end` date default NULL COMMENT 'Value''s Expiration Date', 
PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Member History'; 

members_history.mid là một FK cho id trong bảng thành viên, không phải mọi thành viên sẽ có lịch sử về họ (nhưng cuối cùng tất cả họ sẽ, vì mọi thành viên sẽ phải có một lớp và trạng thái). members_history.value là FK cho members:{members_history.table}.id;

INSERT INTO `members` 
(`id`, `username`, `password`, `salt`, `name_first`, `name_last`, `date_join`, `date_join`) VALUES 
( 1, 'Dygear',MD5('pass'), 's417',  'Mark', 'Tomlin',  DATE(), NULL), 
( 2, 'uberusr',MD5('p455'), '235f',  'Howard', 'Singer',  DATE(), NULL), 
( 3,'kingchief',MD5('leet'), '32fs','Christopher', 'Buckham',  DATE(), NULL); 

INSERT INTO `members:apointed` 
(`id`, `name_prefix`, `hours_extra`, `posisiton`) VALUES 
( 1,   '',   0.00, 'Crew Chief'), 
( 2,   '',   20.00, 'Engineer'), 
( 3,   'Lt.',   40.00, 'Lieutenant'), 
( 4,  'Capt.',   60.00, 'Captin'), 
( 5,  'Chief.',   80.00, '3rd Assistant Chief of Operation'); 

INSERT INTO `members:class` 
(`id`, `class`) VALUES 
( 1, 'Class I'), 
( 2, 'Class II'); 

INSERT INTO `members:elected` 
(`id`, `name_prefix`, `hours_extra`, `posisiton`) VALUES 
( 1,   '',   40.00, 'Trustee'), 
( 2,   '',   40.00, 'Chairman of the Board'), 
( 3,  'Prez.',   40.00, 'President'), 
( 4,  'VPrez.',   40.00, 'Vice-President'), 
( 5,   '',   40.00, 'Recording Secretary'), 
( 6,   '',   40.00, 'Service Secretary'), 
( 7,   '',   40.00, 'Corresponding Secretary'), 
( 8,   '',   40.00, 'Financial Secretary Treasuer'), 
( 9,   '',   40.00, 'Assistant Financial Secretary Treasuer'), 
( 10,  'Chief.',   80.00, 'Chief of Operations'), 
( 11,  'Chief.',   80.00, 'First Deputy Chief of Operations'), 
( 12,  'Chief.',   80.00, 'Second Deputy Chief of Operation'); 

INSERT INTO `members:status` 
(`id`, `status`) VALUES 
( 1, 'Active'), 
( 2, 'Inactive'), 
( 3, 'Student'), 
( 4, 'Probationary'), 
( 5, 'Lifetime'), 
( 6, 'Cadet'), 
( 7, 'Honorary'), 
( 8, 'Medical'), 
( 9, 'Military'), 
( 10, 'Resigned'), 
( 11, 'Disvowed'); 


INSERT INTO `members_history` 
(`id`, `mid`, `table`, `value`, `start`, `end`) VALUES 
(NULL,  1, 'apointed',  3, DATE(), NULL), 
(NULL,  1, 'class',  1, DATE(), NULL), 
(NULL,  1, 'status',  1, DATE(), NULL), 
(NULL,  2, 'elected',  4, DATE(), NULL), 
(NULL,  2, 'class',  1, DATE(), NULL), 
(NULL,  2, 'status',  1, DATE(), NULL), 
(NULL,  3, 'apointed',  10, DATE(), '2010-05-01'), 
(NULL,  3, 'class',  1, DATE(), NULL), 
(NULL,  3, 'status',  1, DATE(), NULL); 

Trả lời

11

Bạn đang sử dụng thiết kế được gọi là các mối liên quan đa hình và thường được thực hiện sai. Các cách để làm cho nó hoạt động là tạo ra một bảng khác, nói members:abstract:

CREATE TABLE IF NOT EXISTS `members:abstract` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
`type` enum('class','elected','appointed','status') NOT NULL, 
    UNIQUE KEY (`id`, `type`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 

Bảng này đóng vai trò như bảng cha cho tất cả các thành viên của mình thuộc tính bảng. Mỗi bảng trong số các bảng này thay đổi khóa chính thành không phải tự động tạo các giá trị id, mà thay vào đó tham chiếu khóa chính của members:abstract. Tôi sẽ chỉ hiển thị members:appointed nhưng những người khác sẽ tương tự.

CREATE TABLE IF NOT EXISTS `members:appointed` (
`id` INT UNSIGNED NOT NULL PRIMARY KEY, -- not auto_increment 
`name_prefix` varchar(8) collate utf8_bin NOT NULL COMMENT 'Prefix Added to Members Name', 
`hours_extra` decimal(4,2) NOT NULL COMMENT 'Hours Given as Bonus for Holding this Position.', 
`position` varchar(40) collate utf8_bin NOT NULL COMMENT 'Name of the Posisiton', 
FOREIGN KEY (`id`) REFERENCES `members:abstract` (`id`) ON DELETE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Undefined within the SOP or By-Laws.'; 

Bạn có thể làm tăng giá trị bảng này tự động tạo ra tự động với một kích hoạt:

DELIMITER // 
DROP TRIGGER IF EXISTS ins_appointed// 
CREATE TRIGGER ins_appointed BEFORE INSERT ON `members:appointed` 
FOR EACH ROW BEGIN 
    INSERT INTO `members:abstract` (`type`) VALUES ('appointed'); 
    SET NEW.id = LAST_INSERT_ID(); 
END; // 
DELIMITER ; 

Làm tương tự cho mỗi bảng thuộc tính khác.

Lưu ý rằng các giá trị id hiện là duy nhất trên tất cả các bảng thuộc tính của bạn.

Tiếp theo, bạn thực hiện members:abstract mục tiêu cho khóa ngoại trong members_history.

CREATE TABLE IF NOT EXISTS `members_history` (
`id` INT unsigned NOT NULL auto_increment COMMENT 'Unique Id', 
`mid` INT unsigned NOT NULL COMMENT 'Members Unique Id.', 
`value` INT UNSIGNED NOT NULL, 
`table` enum('class','elected','appointed','status') NOT NULL, 
`start` date NOT NULL COMMENT 'Value''s Effect Date', 
`end` date default NULL COMMENT 'Value''s Expiration Date', 
PRIMARY KEY (`id`), 
FOREIGN KEY (`mid`) REFERENCES `members` (`id`) ON DELETE CASCADE, 
FOREIGN KEY (`value`, `table`) REFERENCES `members:abstract` (`id`, `type`) ON DELETE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Member History'; 

ý rằng bảng này định nghĩa một chính nước ngoài để bạn không thể tham khảo một id của các loại sai của thuộc tính trong members:abstract.

Bây giờ bạn có thể dựa vào referential integrityquán đó là không thể khi bạn cố gắng thực hiện các hiệp hội đa hình mà không có cha mẹ chung của tất cả các bảng thuộc tính tham chiếu.

Dưới đây là các truy vấn mà trả về kết quả mà bạn mô tả (thử nghiệm trên MySQL 5.1.40):

SELECT m.username, 
    m.password, m.salt, m.name_first, m.name_last, 
    MAX(a.name_prefix) AS name_prefix, 
    COALESCE(MAX(a.hours_extra), MAX(e.hours_extra)) AS hours_extra, 
    MAX(m.date_join) AS date_join, 
    MAX(m.date_leave) AS date_leave, 
    MAX(a.position) AS appointed, 
    MAX(c.class) AS class, 
    MAX(e.position) AS elected, 
    MAX(s.status) AS status 
FROM `members` m 
JOIN `members_history` h ON (h.mid = m.id) 
LEFT OUTER JOIN `members:appointed` a ON (h.table = 'appointed' AND h.value = a.id) 
LEFT OUTER JOIN `members:class` c ON (h.table = 'class' AND h.value = c.id) 
LEFT OUTER JOIN `members:elected` e ON (h.table = 'elected' AND h.value = e.id) 
LEFT OUTER JOIN `members:status` s ON (h.table = 'status' AND h.value = s.id) 
GROUP BY m.id; 
+0

Câu trả lời nổi bật Bill, cảm ơn bạn rất nhiều. Tôi đã học được rất nhiều từ đó. –

+0

Rõ ràng 'trình kích hoạt' không nằm trong quyền hạn của tôi trên máy chủ sản xuất mà tôi sử dụng. Đi tìm huh. –

+0

Trình kích hoạt khá đơn giản, chỉ có hai câu lệnh dài. Bạn có thể làm một cái gì đó tương đương trong mã ứng dụng của bạn mỗi khi bạn chèn vào một trong các bảng của bạn. –

1

tất cả những gì bạn cần là tham gia bên ngoài bên trái cho từng loại lịch sử và bất kỳ logic nào bạn cần để chọn hàng "hiện tại".

cấu trúc bảng của bạn không hoàn toàn hợp lý với tôi để đặt cùng một mẫu cho bạn. có thể nếu bạn cung cấp MỘT thành viên mẫu và một vài hàng lịch sử cho MỘT thuộc tính, tôi có thể giúp bạn.

+0

tôi đã thêm một số chi tiết về giá trị có thể để các bài bản gốc. –

+0

cảm ơn bạn. xin vui lòng xem câu trả lời của hóa đơn ngay bây giờ. – longneck

Các vấn đề liên quan