Денормализация связей многие-ко-многим, через битовые маски
Ситуация довольно банальная: есть статьи и тэги, связанные многие-ко-многим, и необходимо быстро находить статьи с определенным тэгом или несколькими тэгами. Необходимо обойтись без JOIN и одним простым запросом.
В mysql есть тип данных set, который вполне для этого подходит. Но можно и ручками
Посмотрим взлетит оно или нет:
CREATE TABLE `tests`.`article` (
`id` int(10) unsigned NOT NULL auto_increment,
`mask` int(10) unsigned default NULL,
`cset` set('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20') default NULL,
PRIMARY KEY (`id`),
KEY `mask` (`mask`),
KEY `cset` (`cset`)
) ENGINE=InnoDB;
Добавление записей:
INSERT INTO article VALUES(NULL, 3, '1,2');
Выбор по одному тэгу:
select count(*) from article where mask & 1; //помеченные тэгом #1
select count(*) from article where cset & 1;
Выбор по нескольким тэгам – объединение:
select count(*) from article where mask & 3; //помеченные тэгом #1 ИЛИ #2
select count(*) from article where cset & 3;
Выбор по нескольким тэгам – пересечение:
select count(*) from article where mask & 1 AND mask & 2; //помеченные и тэгом #1 И тэгом #2
select count(*) from article where cset & 1 AND cset & 2;
По скорости варианты равноценны, и на табличке с 500К записей (20 разных тэгов, по 1-20 тэгов на статью) все запросы обрабатывались за, примерно, 0.3 секунды, на средненькой домашней машинке.
ну если закрыть глаза на ограничение в 64 значения…
Да ну не такое уж строгое ограничение, если судить по реальной жизни. Да и никто не мешает добавить еще одно поле и спрятать работу с ним, в каком-нибудь criteria-классе.