Как сравнить данные с различным количеством значений

Я постараюсь объяснить мою проблему так хорошо, как могу.

Я должен сравнить данные того же типа, сохраненные в базе данных MySql, но содержащие различное количество значений.

Я строю свою базу данных так (вероятно, не самая лучшая):

--
-- Table structure for table `amount`
--

CREATE TABLE `amount` (
`id` tinyint(1) UNSIGNED NOT NULL,
`value` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `amount`
--

INSERT INTO `amount` (`id`, `value`) VALUES
(34, 1),
(22, 2),
(30, 6),
(21, 7),
(9, 8),
(17, 9),
(10, 10),
(15, 11),
(3, 12),
(4, 13),
(8, 14),
(5, 15),
(16, 16),
(13, 17),
(6, 18),
(20, 19),
(7, 20),
(23, 21),
(18, 22),
(19, 23),
(24, 24),
(14, 25),
(25, 26),
(26, 27),
(28, 28),
(29, 29),
(11, 30),
(27, 31),
(12, 32),
(31, 33),
(32, 35),
(33, 36),
(2, 98),
(1, 99);

-- --------------------------------------------------------

--
-- Table structure for table `mark`
--

CREATE TABLE `mark` (
`id` tinyint(1) UNSIGNED NOT NULL,
`name` varchar(16) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `mark`
--

INSERT INTO `mark` (`id`, `name`) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D'),
(5, 'E'),
(6, 'F'),
(7, 'G'),
(8, 'H'),
(9, 'I'),
(10, 'J')
(11, 'K')
(12, 'L')
(13, 'M')
(14, 'N')
(15, 'O');

-- --------------------------------------------------------

--
-- Table structure for table `profile`
--

CREATE TABLE `profile` (
`id` smallint(2) UNSIGNED NOT NULL,
`run` smallint(2) NOT NULL,
`deleted` datetime DEFAULT NULL,
`created` datetime NOT NULL,
`validated` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
-- Table structure for table `profile_mark`
--

CREATE TABLE `profile_mark` (
`id` int(11) NOT NULL,
`id_profile` smallint(2) UNSIGNED NOT NULL,
`id_mark` tinyint(1) UNSIGNED NOT NULL,
`id_amount` tinyint(1) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Каждый раз, когда поступают новые данные, создается профиль и, если необходимо, новая отметка и новая сумма.

  • Профиль имеет фиксированный номер отметки (9, 16, 24)
  • Для каждого профиля каждая его отметка может иметь 0, 1 или 2 суммы

Это означает, что профиль может иметь до 48 значений. Я планирую иметь как минимум 20000 профилей в базе данных в будущем.

Моя цель: Если я выберу один профиль, я должен найти все другие профили, которые имеют хотя бы 1 общее значение для Х их оценок. (где X — минимальное количество знаков, которое должно совпадать)

Сейчас я беру все профили один за другим, чтобы сравнить их с проверенным. Это занимает некоторое время (у меня сейчас только около 50 профилей в базе данных), и это не очень хорошее решение для будущего моего приложения.

Другое решение, которое я представляю, — это кэширование (или сохранение в базе данных) всех идентификаторов профиля для каждой ассоциации mark_amount … но, похоже, это не очень хорошая идея 🙁

Мне нужно несколько советов, чтобы оптимизировать это сравнение, пожалуйста.
(Я открыт для другой базы данных, кеш-системы, чем php / mysql и т. Д …)

EDIT1: пример соответствия профиля или нет на 8 баллов

https://jsfiddle.net/gafy2w4k/

2

Решение

Запрос на возврат всех profile_mark.id_profile который имеет именно так @matched_marks оценки, по крайней мере, на 1 ту же сумму, что и профиль с данным @target_profile_id:

SELECT `match`.id_profile, count(*) as X FROM (
SELECT DISTINCT `all`.id_profile, `all`.id_mark FROM profile_mark as `all`
INNER JOIN profile_mark as `one`
ON `one`.id_mark = `all`.id_mark
AND `one`.id_amount = `all`.id_amount
WHERE `all`.id_profile <> @target_profile_id
AND `one`.id_profile = @target_profile_id
) as `match`
GROUP BY 1
HAVING X = @matched_marks; // can be >= if you need at least X matching marks

Как примечание стороны, id_profile smallint(2) кажется недостаточным для не менее 20000 профилей.

2

Другие решения

Вопрос требует более подробной информации, но я вижу несколько общих улучшений, которые будут полезны здесь:
Во-первых, я не вижу никаких индексов, пожалуйста, сделайте PRIMARY KEY для каждого идентификатора
Пример :

CREATE TABLE `mark` (
`id` tinyint(1) UNSIGNED NOT NULL PRIMARY KEY,
...

Если уже слишком поздно менять таблицы, используйте CREATE INDEX

Во-вторых, для согласованности сделайте ССЫЛКИ, чтобы объявить зависимость внешнего ключа
пример:

FOREIGN KEY (id_mark) REFERENCES mark(id)

Наконец, беги EXPLAIN в своем запросе и посмотрите, что вы можете улучшить в соответствии с результатом (вы можете добавить индекс для запросов, к которым часто привыкли)
EXPLAIN SELECT ...

0