иллюстрация к моему посту выше Код (Text): CREATE TABLE `lions` ( `id` int(10) NOT NULL AUTO_INCREMENT, `sex` tinyint(1) NOT NULL DEFAULT '1', `wool` smallint(3) NOT NULL DEFAULT '1', `color` smallint(3) NOT NULL DEFAULT '1', PRIMARY KEY (`id`), KEY `ix_lions_2` (`wool`,`color`) ) ENGINE=InnoDB нагенерил 1млн строк как Код (PHP): $stmt = $db->prepare("INSERT INTO `lions`(`sex`, `wool`, `color`) VALUES(?, ?, ?)"); mt_srand(1); for ($i = 0; $i < 1000000; ++$i) { $stmt->execute([ 1 + $i % 2, mt_rand(1, 100), mt_rand(1, 100) ]); } у нас 4 возможных сочетания признаков wool и color, два из которых имеют равный приоритет. запрос #1 (макс. приоритет) Код (Text): SELECT SQL_NO_CACHE m.`id` AS m_id, f.`id` AS f_id FROM `lions` AS m, `lions` AS f WHERE (m.wool = f.wool) AND (m.color = f.color) AND (m.`sex` = 1) AND (f.`sex` = 2) терпения не хватает дождаться завершения запроса но если добавить LIMIT 50, то time 0.002s mt_srand() для повторяемости результата SQL_NO_CACHE чтобы не одурачить себя теперь четырежды повторяю этот запрос (c limit) но с вариантами =/<> у wool и color и склеиваю через UNION ALL (... = AND =) U.A. (... = AND <>) U.A. (... <> AND =) U.A. (... <> AND <>) получаю за 0.036s из пула в миллион записей! порядок строк сохранится в порядке расстановки запросов, т.е. приоритетом 4х вариантов сочетаний я рулю как мне захочется! Добавлено спустя 31 минуту 39 секунд: Как это улучшить чтобы добиться уникальности самца и самки я еще не придумал ))) Добавлено спустя 2 минуты 20 секунд: вот так можно: Код (Text): SELECT SQL_NO_CACHE m.`id` AS m_id, MIN(f.`id`) AS f_id FROM `lions` AS m, `lions` AS f WHERE (m.wool = f.wool) AND (m.color = f.color) AND (m.`sex` = 1) AND (f.`sex` = 2) GROUP BY m.`id` LIMIT 50 time 0.021s вполне приемлемо Добавлено спустя 12 минут 59 секунд: -------------------------------------------- У меня в голове есть и абсолютно другой вариант оптимизации У нас ограниченное число сочетаний параметров wool, color и sex. 100*100*2 = 20000. По условию задачи, при прочих равных нас интересуют те записи, у которых id меньше. Отсюда следует, что будь у нас хоть миллион, хоть 100 миллионов львов, реально в наших выборках будут присутствовать <= 20000 кандидатов. То есть мы можем заранее сгруппировать записи чтобы уменьшить область определения. Код (Text): SELECT MIN(id), wool, color, sex FROM lions GROUP BY wool, color, sex Мысль понятна?
лев льву не пара и львица львице не пара - 10201 комбинаций. Только у заказчика в таблице у львиц шерсть короткая (я думаю от 0 до 10ти), а у львов длинная (например от 30 до 100) Мне кто-нибудь может пояснить смысл фразы
Maputo, ты не о том говоришь. При чем здесь гомо/гетеро?! Математически 20000 вариантов пол+длина+цвет. Заказчик указал все возможные вариации в задании. Откуда ты взял про короткую шерсть? У нас условные сферические львы. Не надо додумывать типа "из жизни".
Вот дамп на 2000 записей http://pesow.com/F/test_09-2015-08-07.sql.gz Вот "правильный" универсальный/классический запрос , который даёт правильный ответ: Код (PHP): select t1.*, t2.*, t.rating from ( -- пересечение таблиц, что ооочень много. Квадратичная зависимость. Для 500х500 (1000 в таблице) = ~250000 чтений строк. 10 000 в таблице = ~25 млн. чтений. select t1.id id1, (abs(t1.wool - t2.wool) + abs(t1.color - t2.color)) rating, min(t2.id) id2, count(*) c from test_09 t1 left join test_09 t2 on t2.sex = 2 -- львицы where t1.sex = 1 -- львы group by t1.id, rating order by rating, id1 limit 50 ) t left join test_09 t1 on t1.id = t.id1 left join test_09 t2 on t2.id = t.id2 artoodetoo, можете сравнить результат этого запроса с результатом Вашего метода - если равны, значит Ваш метод правильный. Скорее всего, правильный.
По невнимательности я пропустил тот факт, что значения wool и color начинаются с 0, а не с 1. То есть 101 вариант. Но сути это не меняет, запросы остаются те же. Просто область определения шире. Добавлено спустя 27 минут 8 секунд: Chushkin, вот с этим я не согласен: Код (Text): (abs(t1.wool - t2.wool) + abs(t1.color - t2.color)) rating разве было указание, что "расстояние" между параметрами имеет значение? Я так понял задание, что важно совпадение или несовпадение. То есть если вычислять рейтинг как число то Код (Text): ((t1.wool = t2.wool) + (t1.color = t2.color)) rating Другой повод для несовпадения результатов — указание на то, что "признаки wool и color имеют одинковый вес". То есть записи, где только один из признаков совпадает, могут стоять в любом порядке относительно друг друга, это не будет нарушением. То есть возможны разные правильные варианты ответа.
Не хочу постить новое сообщение. Напишу тут. Представьте себе таблицу на 10м записей заполненных рандомом с одинаковыми параметрами для львов и львиц. В ней для любого льва можно будет найти львицу со 100% совпадением перебрав для каждого не более 20402 записей. Запрос для такого поиска будет слишком простым. Вы уже убедились, что задание составлено очень грамотным человеком. Поэтому в таблице скорее всего нет 100% совпадающих пар, а если и есть - их должно быть значительно меньше 25 пар. Как заполнить таблицу в 10м записей, чтобы там было так мало идеальных пар? Очень просто - диапазон значений для шерсти у львов и у львиц выбрать разный. Вариант 1: диапазоны пересекаются 25 пар со 100% совпадением будет легко найти Вариант 2(очень возможный): диапазоны имеют одно общее граничное значение (0-50, 50-100) В этом случае на 10 м записей (при равномерном рандоме) получается не менее 200 - 250 идеальных пар. Это не есть хорошо, но уменьшить принудительно это число можно Вариант 3 (наиболее вероятный): диапазоны значений не пересекаются. В этом случае можно изменить вручную 2-3 записи, чтобы было 2-3 пары со 100% совпадением. При самом худшем раскладе (0-50, 50-100) - для поиска наиболее подходящей пары достаточно для одной особи перебрать не более 10,3к записей. Наиболее вероятны диапазоны 0-49, 50-100 - это 10,2к записей. P.S.: Мне кажется нерентабельным делить таблицу на две, только ради того, чтобы в одном столбце были львы, а в другом львицы. P.P.S.: Я уже писал, но еще раз повторю - в задании дано четкое условие, что львы и львицы в парах должны быть уникальны.
Читаем задание: "Абсолютно подходящие пары - wool и color совпадают на 100% . Самые неподходящие - с максимальными различиями." Т.е. макс.рейтинг = abs(t1.wool - t2.wool) = 0 (это и есть "совпадение"), мин.рейтинг. = abs(t1.wool - t2.wool) = 101 (максимальное различие) Условие "Оба признака (wool и color) имеют одинковый вес." просто говорит о том, что не важно в каком порядке их учитывать. Т.е. рейтинг (wool=100 и color=0) = (wool=0 и color=100).
Соглашусь, что формулировка допускает толкование, но я остаюсь при мнении, что: "максимальные различия" это случай когда оба признака не равны. А 100% совпадение это когда оба признака равны. Не вижу указания расчитывать разницу в длине или цвете.
Я называю разницей математическую операцию "-" ))) В тексте задания слово "разница" не встречается. А различием называю простой факт неравенства. Думаю, что заказчик имеет в виду то же. Не будем ходить вокруг да около. Кто-то должен спросить автора, ящитаю.
С учётом моего понимания фразы "Абсолютно подходящие пары - wool и color совпадают на 100% .Самые неподходящие - с максимальными различиями." (т.е. от 100% до 0% совпадения), я считал степень различия. А уж в каких попугаях она считается и как - дело десятое. В данном конкретном случае, с учётом, что исходные данные целое число, оптимальны арифметические операции, я так думаю. И да, я уверен на 99,99%, что автор задания имел в виду "от 100% до 0%".
ok, это достойная т.з. в задании есть еще место для разночтений: "Если для одного льва находится несколько подходящих в равной степени львиц, то берем ту, у которой меньший номер. Аналогично должно работать и с множеством львов у львицы." (выделено мной) Могут ли в выборке присутствовать пары с одним и тем же львом, но с разной степенью подходящести и разными львицами? То есть такие записи: rate, m_id, f_id 1, 14, 20 1, 17, 88 1, 78, 1020 2, 14, 99 2, 101, 104 Это неочевидно. Добавлено спустя 6 минут 44 секунды: P.S. Я выложил на sqlfiddle версию с группирующей вьюшкой. Львы и львицы, подходящие в равной степени, отскаются на этапе create view — остаются только наименьшие id. Потом по этой выборке делаются запросы в порядке убывания приоритета. Случаи пар с разными рейтингами, где один из партнеров повторяется, возможны! http://sqlfiddle.com/#!9/4c991/1 По понятным причинам, миллион записей на sqlfiddle не загонишь. Демо с 500 львами. У меня на миллионной таблице запрос без кеша выполняется за 4 секунды. P.P.S. Я не считаю этот вариант правильным и окончательным. Буду благодарен за подсказки. P.P.P.S. Chushkin, насколько я вижу, у вас не отбрасываются повторы львов и львиц даже в пределах одного рейтинга. Так что ваш вариант на эталон тоже не тянет.
Эээ, вообще-то "...несколько подходящих в равной степени..." выполняется, в подзапросе с "group by t1.id, rating". Добавлено спустя 1 минуту 58 секунд: Это сарказм? Добавлено спустя 28 минут 14 секунд: artoodetoo, сравнил Ваш вариант со своим: на 100% (т.е. у меня rating = 0) совпадает, остальное не совпадает. Возможно из-за того, что алгоритмы расчёта "подходящих" разные. (кстати, малость поправил мой запрос /сортировку/)
Я так понял все забили на это задание. Тогда выкладываю свой вариант решения. Должен выбрать 3 пары с рейтингом 0, а все остальные с рейтингом 1. Числа взяты не из головы, а продиктованы логикой и здравым смыслом. Естественно первые два куска запроса должны выглядеть иначе, но выберут те же самые индексы. Код (PHP): (SELECT *, (abs(s1.wool - s2.wool) + abs(s1.color - s2.color)) as rating FROM `lions` as s1 LEFT JOIN `lions` as s2 on (s2.id = (s1.id + 5099)) WHere s1.id = 1) UNION (SELECT *, (abs(s1.wool - s2.wool) + abs(s1.color - s2.color)) as rating FROM `lions` as s1 LEFT JOIN `lions` as s2 on (s2.id = (s1.id + 29)) WHere s1.id = 5101 OR s1.id = 5131 OR s1.id = 5161 OR s1.id = 5191 OR s1.id = 5221 OR s1.id = 5251 OR s1.id = 5281 OR s1.id = 5311 OR s1.id = 5341 OR s1.id = 5371 OR s1.id = 5401 OR s1.id = 5431 OR s1.id = 5461 OR s1.id = 5491 OR s1.id = 5521 OR s1.id = 5551 OR s1.id = 5581 OR s1.id = 5611 OR s1.id = 5641 OR s1.id = 5671 OR s1.id = 5701 OR s1.id = 5731 OR s1.id = 5761 OR s1.id = 5791) UNION (SELECT *, (abs(s1.wool - s2.wool) + abs(s1.color - s2.color)) as rating FROM `lions` as s1 LEFT JOIN `lions` as s2 on (s1.id = (s2.id + 1)) WHere s2.id = 10000000) ORDER BY rating LIMIT 0,25 На самом деле все может быть совсем не так. НО... Можете удалить, если считаете полной лажей.