6

単一のテーブルの (メンバー) 列に相互参照したい 4 つのテーブル (任命、クラス、選出、ステータス) があります。4 つのテーブルの値は、履歴テーブル (members_history) に基づいて時間依存です。望ましい結果は、クエリがすべてのメンバーと、メンバー行内の現在の任命された役職または現在の選択された役職、クラス、およびステータスを出力し、外部行から取得した追加情報を含めることです。

したがって、単に返す代わりに:

ID、ユーザー名、パスワード、ソルト、name_first、name_last、date_join、date_leave;

クエリは返されます

ID、ユーザー名、パスワード、salt、name_prefixname_first、name_last、 date_join 、hours_extradate_leave 、、、& ;appointedclasselectedstatus

追加された列が履歴に現在の値を持っていない場合は常に、その結​​果は NULL になります。

サブクエリでこれを行うことができると思いますが、これまでキーボードに頭をぶつけていました。後でもう一度試してみますが、それまでは、試してくれる人、または私を正しい方向に向けようとする人はいますか?

私のSQL(しゃれは意図されていません)テーブルの構造は次のとおりです。

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 は members テーブルの id の FK であり、すべてのメンバーが履歴を持っているわけではありません (ただし、すべてのメンバーがクラスとステータスを持っている必要があるため、最終的にはすべてがそうです)。members_history.valueの FK です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);
4

2 に答える 2

13

多態的関連付けと呼ばれる設計を使用していますが、これは頻繁に間違って行われます。それを機能させる方法は、別のテーブルを作成すること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;

このテーブルは、すべてのメンバー属性テーブルの親テーブルとして機能します。これらの各テーブルは、ID値を自動的に生成しないように主キーを変更しますが、代わりにの主キーを参照しますmembers:abstract。表示しますmembers:appointedが、他の部分も同様です。

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.';

このテーブルに、トリガーを使用して自動生成された値を自動的に取得させることができます。

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 ;

他の各属性テーブルについても同じようにします。

id値は、すべての属性テーブルで一意になっていることに注意してください。

次にmembers:abstract、で外部キーのターゲットを作成し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';

このテーブルは外部キーを定義しているため、で間違ったタイプの属性のIDを参照できないことに注意してくださいmembers:abstract

これで、参照整合性一貫性に依存できます。これは、参照されるすべての属性テーブルの共通の親なしでポリモーフィックな関連付けを実装しようとする場合には不可能です。

説明した結果(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;
于 2009-11-03T06:58:18.843 に答える
2

必要なのは、各履歴タイプの左外部結合と、「現在の」行を選択するために必要なロジックだけです。

あなたのテーブル構造は、サンプルをまとめるのに十分な意味を持ちません。1 つのサンプル メンバーと 1 つの属性の 2 行の履歴を提供していただければ、私がお手伝いできます。

于 2009-11-02T21:03:19.640 に答える