Всем привет. Нужно вывести 2 случайных значения из базы с учетом частоты показов. Это необходимо для того, что бы в первую очередь выводились случайные значения с самым маленьким коэффициентом. В переменных $q51,$q52 хранятся значения выведенных фраз. Алгоритм: 1. Увеличиваем количество показов у фраз $q51,$q52 2. Находим среднее арифметическое по полю counts (количество показов) 3. Получаем количество показов у фраз $q51,$q52 4. Делим число показов на среднее арифметическое 5. записываем коэффициент в поле ratio Вопрос: 1. При вызове функции запись в поля counts и ratio не происходит. При этом ошибок никаких не вылетает. В чем может быть косяк? 2. Нужно, чтобы в поле ratio записывалась десятичная дробь. Правильно ли использовать bcdiv в этом случае? я ее никогда не применял. PHP: function Phrases(){ global $conn,$q51,$q52; //увеличиваем на 1 поле показа для выведенных значений $queryCountShow = "UPDATE phrases SET counts = сounts + 1 WHERE phrase = $q51 AND $q52"; $resultCountShow = $conn->query($queryCountShow); //находим среднее значение по полю показа $queryAvg_val = "SELECT AVG(counts) FROM phrases"; $resultAvg_val = $conn->query($queryAvg_val); $y = mysqli_fetch_row($resultAvg_val); //рассчитываем коэффициент показа для выведенных значений for ($i=1; $i <= 2 ; $i++) { $queryCount = "SELECT counts FROM phrases WHERE (phrase = ${'$q5'.$i})"; //берем значение поля показа $resultCount = $conn->query($queryCount); $x = mysqli_fetch_row($resultCount); $ratio = bcdiv((string)$x[0], (string)$y[0], 4); //вычисляем коэффициент показа (4 знака после запятой) $queryRatio = "UPDATE prases SET ratio = $ratio WHERE (phrase = ${'$q5'.$i})"; $resultRatio = $conn->query($queryRatio); } if (!$resultCountShow || !$resultAvg_val || !$resultCount || !$resultRatio) echo "Сбой при обращении или записи в таблицу Фразы в базе данных <br>". $conn->error . "<br><br>"; }
Синтаксис sql запроса вызывает большое сомнение. Покажи какой запрос в итоге получается. Включи вывод ошибок
Какова изначальная задача? Допустим в бд 10 записей без показов, выбрали 2 случайных, увеличили количество показов, следующая выборка должна быть из 8 оставшихся? И так далее до показа всех из 10?
@AlexandrS, там где есть коэффициент показов, там уже и не рандомность вовсе. Опять же я ещё не закончил с вопросами. Это только прелюдия.
Вот дамп. Спасибо за подсказку на счет ошибок)) совсем забыл про это. Запрос подправил, теперь выглядит как на скрине --- Добавлено --- --- Добавлено --- Да все верно. При изменении хотя бы в одном поле значения коэффициента повлечет изменение всех коэффициентов во всем поле. Таким образом всегда будут выбираться только те записи, коэффициент которых < 1. Рандомность при этом сохраняется. Просто из выборки исключаются несколько значений с коэффициентом >1
Сразу скажу, что топик по теме, но не совсем то, что нужно топикстартеру. Короче, рандомная выборка с учетом веса записи в pgsql. Го. Для начала, портянка говнокода, если вдруг кто захочет потестить. PHP: <?php try { $db = new PDO("pgsql:dbname=test;host=localhost", "postgres", ""); test($db); } catch (PDOException $e) { echo $e->getMessage(); } function create(PDO $db) { $db->exec("alter sequence table_name_id_seq RESTART"); $db->exec("TRUNCATE TABLE random_with_weight"); for($i = 0; $i <= 1000; $i++) { $values = []; for ($k = 0; $k <= 1000; $k++) { $rand = rand(1, 5); $values[] = "($rand)"; } $values = implode(',', $values); $db->exec("INSERT INTO random_with_weight (weight) VALUES $values"); echo $i . "\r"; } } function test(PDO $db) { $values = []; for ($i = 0; $i <= 100; $i++) { $result = $db->query("SELECT weight, count(weight) AS cnt FROM (SELECT * FROM random_with_weight ORDER BY random() / weight LIMIT 100) as rand GROUP BY weight")->fetchAll(PDO::FETCH_ASSOC); foreach ($result as $item) { if (!isset($values[$item['weight']])) { $values[$item['weight']] = 0; } $values[$item['weight']] += $item['cnt']; } echo $i . "\r"; } var_dump($values); } Как видно, в таблице вида (id, weight) ~1кк записей с весом от 1 до 5 включительно. Результат ORDER BY random() Код (Text): array(5) { [1]=> int(2067) [2]=> int(2007) [3]=> int(2026) [4]=> int(1932) [5]=> int(2068) } Результат ORDER BY random() / weight Код (Text): array(5) { [1]=> int(694) [2]=> int(1376) [3]=> int(2021) [4]=> int(2632) [5]=> int(3377) } Собственно, что и требовалось - рандом присутствует, но в выборке чаще попадаются более "тяжелые" записи. Единственная проблема которая приходит в голову - это скорость, как известно, ORDER BY RAND не самый дешевый запрос. И действительно, random() выполняется за ~250мс, a random() / weight так вообще ~300мс. Долго, короче. Код (Text): Limit (cost=60264.78..60265.03 rows=100 width=16) (actual time=296.310..296.328 rows=100 loops=1) -> Sort (cost=60264.78..62769.78 rows=1002001 width=16) (actual time=296.308..296.320 rows=100 loops=1) Sort Key: ((random() / (weight)::double precision)) Sort Method: top-N heapsort Memory: 29kB -> Seq Scan on random_with_weight (cost=0.00..21969.02 rows=1002001 width=16) (actual time=0.012..178.947 rows=1002001 loops=1) Planning time: 0.089 ms Execution time: 296.353 ms Саму по себе скорость выполнения запроса не уменьшить, ведь для рандомной выборки придется так или иначе перебрать всю таблицу и потому конкретный способ оптимизации зависит исключительно от таска. Можно, к примеру, задать дополнительные условия, ограничивающие выборку: Код (Text): EXPLAIN ANALYSE SELECT id, weight FROM random_with_weight WHERE id < 200000 ORDER BY random() / weight LIMIT 100; Limit (cost=15610.31..15610.56 rows=100 width=16) (actual time=83.899..83.918 rows=100 loops=1) -> Sort (cost=15610.31..16106.52 rows=198482 width=16) (actual time=83.897..83.907 rows=100 loops=1) Sort Key: ((random() / (weight)::double precision)) Sort Method: top-N heapsort Memory: 29kB -> Index Scan using table_name_pk on random_with_weight (cost=0.42..8024.47 rows=198482 width=16) (actual time=0.018..58.861 rows=199999 loops=1) Index Cond: (id < 200000) Planning time: 0.085 ms Execution time: 83.941 ms Ну или брать не всю таблицу, а лишь её части (https://habr.com/ru/post/266759/) Код (Text): EXPLAIN ANALYSE SELECT id, weight FROM random_with_weight TABLESAMPLE bernoulli(5) ORDER BY random() / weight LIMIT 100; Limit (cost=7225.54..7225.79 rows=100 width=16) (actual time=35.075..35.094 rows=100 loops=1) -> Sort (cost=7225.54..7350.79 rows=50100 width=16) (actual time=35.074..35.089 rows=100 loops=1) Sort Key: ((random() / (weight)::double precision)) Sort Method: top-N heapsort Memory: 29kB -> Sample Scan on random_with_weight (cost=0.00..5310.75 rows=50100 width=16) (actual time=0.013..28.675 rows=50011 loops=1) Sampling: bernoulli ('5'::real) Planning time: 0.068 ms Execution time: 35.121 ms Код (Text): EXPLAIN ANALYSE SELECT id, weight FROM random_with_weight TABLESAMPLE system(5) ORDER BY random() / weight LIMIT 100; Limit (cost=3679.54..3679.79 rows=100 width=16) (actual time=16.768..16.784 rows=100 loops=1) -> Sort (cost=3679.54..3804.79 rows=50100 width=16) (actual time=16.767..16.778 rows=100 loops=1) Sort Key: ((random() / (weight)::double precision)) Sort Method: top-N heapsort Memory: 29kB -> Sample Scan on random_with_weight (cost=0.00..1764.75 rows=50100 width=16) (actual time=0.014..9.792 rows=50172 loops=1) Sampling: system ('5'::real) Planning time: 0.068 ms Execution time: 16.816 ms Но тут уже надо понимать, что мы имеем дело с псевдослучайностью и лишь с кусками таблицы. К примеру, если количество записей с weight=5 существенно меньше остальных, то в выборке их может и не появиться вовсе, потому как вес вроде способствует более частому появлению, но в изначальную выборку он просто не попал. Короче, возможны варианты ) Такие дела.
Ты что-то больно мудреное замутил для 2 случайных значений из базы. Касательно кода: PHP: "UPDATE prases SET ratio = $ratio WHERE (phrase = ${'$q5'.$i})"; Это хрень какая-то. Я даже понять не могу точно, как в итоге запрос выглядеть будет. Ты попроще старайся делать. Насколько я смог догадаться тебе нужно выводить 2 наиболее редких по показам сообщения. Самые редкие сообщения имеют самые низкие показы, зачем тебе тут вообще ratio? Лишнее поле. Дальше задача сводится к тому, чтобы показать 2 сообщения из таблицы отсортированной по возрастанию показов. Вот тебе простые и быстрые запросы. PHP: $mysqli = new mysqli("localhost", "my_user", "my_password", "world"); $sql = "SELECT * FROM table ORDER BY counts ASC LIMIT 2"; /* этот простой запрос покажет 2 самых редких сообщений */ $res = $mysqli->query($sql); /* предположим тут ты сложишь номера показанных сообщений */ $string = implode(", ", $ids); /*это расставит значения в строку через запятую*/ $update = "UPDATE table SET counts = counts + 1 WHERE id in($string)"; /*увеличиваешь показы для показанных сообщений*/