Вопрос

У меня есть 4 таблицы (назначенный, класс, избранный, статус), на которые я хочу перекрестно ссылаться в столбце одной таблицы (члены).Значения из 4 таблиц чувствительны ко времени и основаны на таблице истории (members_history).Желаемый результат состоит в том, что запрос должен выводить всех членов и текущую назначенную должность или текущую выборную должность, класс и статус в строке участников, а также включать дополнительную информацию, полученную из внешних строк.

Итак, вместо того, чтобы просто вернуться:

идентификатор, имя пользователя, пароль, соль, name_first, name_last, date_join и date_leave;

Запрос вернет

идентификатор, имя пользователя, пароль, соль, name_prefix, имя_первое, имя_последнее, hours_extra, date_join, date_leave, appointed, class, elected & status;

Если добавленный столбец не имеет текущего значения в истории, его результат должен быть 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 — это FK для идентификатора в таблице участников, не у каждого участника будет своя история (но в конечном итоге она будет у всех, поскольку у каждого участника должен быть класс и статус). members_history.value это ФК для 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);
Это было полезно?

Решение

Вы используете дизайн под названием полиморфные ассоциации и это часто делается неправильно.Чтобы заставить это работать, нужно создать еще одну таблицу, скажем 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;

Эта таблица служит родительской таблицей для всех таблиц атрибутов ваших участников.Каждая из этих таблиц меняет свой первичный ключ на нет генерировать значения идентификаторов автоматически, но вместо этого ссылаться на первичный ключ 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 ;

Сделайте то же самое для каждой из остальных таблиц атрибутов.

Обратите внимание, что значения идентификаторов теперь уникальны во всех таблицах атрибутов.

Далее вы сделаете 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';

Обратите внимание, что в этой таблице определен внешний ключ, поэтому вы не мочь ссылаться на идентификатор неправильного типа атрибута в 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;

Другие советы

все, что вам нужно, это левое внешнее соединение для каждого типа истории и любая логика, которая вам нужна для выбора «текущей» строки.

Структура вашей таблицы не имеет для меня достаточно смысла, чтобы составить для вас образец.возможно, если вы предоставите ОДИН образец элемента и пару строк истории для ОДНОГО атрибута, я смогу вам помочь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top