Показать элемент дня

Я ищу, чтобы создать функцию, которая получает мне случайный элемент из таблицы mySQL, но давайте мне сохранить возвращаемый как «элемент дня». Другими словами, элемент, который был «элементом дня» вчера, не должен отображаться снова, пока все остальные предметы не будут показаны как элемент дня.

Любые предложения о том, как это сделать элегантным способом?

благодаря

Добавьте столбец bool " UsedAsItemOfTheDay " в значение false (0). Обновить до значения true при выборе элемента. Исключить уже используемые элементы из процесса выбора.

 SELECT * FROM `table` WHERE UsedAsItemOfTheDay = 0 ORDER BY RAND() LIMIT 1; 

(Примечание: это не самый быстрый способ вернуть случайную строку в MySql, она будет медленной на огромных таблицах)

См. Также: быстрый выбор случайной строки из большой таблицы в mysql

SELECT <fields> FROM <table> WHERE <some logic to exclude already used> ORDER BY RAND() LIMIT 1 приведет к случайной строке из таблицы.

Добавьте столбец для хранения, был ли элемент использован:

 ALTER TABLE your_table ADD COLUMN isused BOOL DEFAULT 0; 

Получите случайный элемент дня:

  SELECT t.* FROM your_table t WHERE t.isused = 0 ORDER BY RAND() LIMIT 1 

Теперь обновите эту запись, чтобы она не могла использоваться в будущем:

 UPDATE your_table SET isused = 1 WHERE id = id_from_select_random_statement 

Люди, которые «знают» SQL, будут искать декларативные решения и будут избегать процедурного кода. Флагирование строк – «запах» для процедурного кода.

Является ли набор Items статичным (никогда не меняющимся) или стабильным (редко изменяется)? Если да, было бы проще сделать одноразовое упражнение генерации таблицы соответствия значений с этого момента до конца времени, вместо того, чтобы планировать выполнение цикла для ежедневного поиска неиспользуемых флагов и обновления флага на сегодняшний день и очистки все флаги, если все были использованы и т. д.

Создайте таблицу последовательных дат между сегодняшней датой и датой будущего, обозначающей срок службы вашего приложения (конечно, вы можете считать, что упускаете нерабочие дни). Добавьте столбец (столбцы), ссылающиеся на ключ в таблице Items (убедитесь, что вы ON DELETE NO ACTION ссылочное действие ON DELETE NO ACTION только в том случае, если эти Items оказались не статичными!) Затем произвольно присваивайте весь набор Items один в день, пока каждый из них не будет был использован один раз. Повторите попытку для всего набора Items пока таблица не будет заполнена. Вы можете легко сгенерировать эти данные с помощью электронной таблицы и импортировать ее (или чистый SQL, если вы хардкор;)

Быстрый пример с использованием стандартного SQL:

Скажем, в наборе всего пять Items :

 CREATE TABLE Items ( item_ID INTEGER NOT NULL UNIQUE ); INSERT INTO Items (item_ID) VALUES (1), (2), (3), (4), (5); 

Таблица поиска выглядит так просто:

 CREATE TABLE ItemsOfTheDay ( cal_date DATE NOT NULL UNIQUE, item_ID INTEGER NOT NULL REFERENCES Items (item_ID) ON DELETE NO ACTION ON UPDATE CASCADE ); 

Начиная с сегодняшнего дня, добавьте весь набор Items в произвольном порядке:

 INSERT INTO Items (item_ID) VALUES ('2010-07-13', 2), ('2010-07-14', 4), ('2010-07-15', 5), ('2010-07-16', 1), ('2010-07-17', 3); 

Затем, начиная с самой последней незаполненной даты, добавьте весь набор Items в (надеюсь, другой) случайный порядок:

 INSERT INTO Items (item_ID) VALUES ('2010-07-18', 1), ('2010-07-19', 3), ('2010-07-20', 4), ('2010-07-21', 5), ('2010-07-22', 2); 

…и опять…

 INSERT INTO Items (item_ID) VALUES ('2010-07-23', 2), ('2010-07-24', 3), ('2010-07-25', 5), ('2010-07-26', 1), ('2010-07-27', 4); 

.. и так далее, пока таблица не будет заполнена.

Тогда было бы просто случай поиска сегодняшней даты в таблице поиска по мере необходимости.

Если набор Items изменится, таблица поиска, очевидно, потребуется восстановить, поэтому вам нужно сбалансировать простоту дизайна и необходимость ручного обслуживания.

Если у вас есть фиксированные элементы, вы можете добавить столбец

 ALTER TABLE your_table ADD COLUMN item_day INT DEFAULT 0; 

затем выбор использования элемента

 WHERE item_day = DATE_FORMAT('%j') 

Если вы получите пустой результат, вы можете отформатировать новый список дневных элементов:

 <?php $qry = " UPDATE your_table SET item_day = 0"; $db->execute($qry); // You only need 355 item to set as item of the day for($i = 0; $i < 355; $i++) { $qry = "UPDATE your_table SET item_day = ".($i+1)." WHERE item_day = 0 ORDER BY RAND() LIMIT 1"; $rs = $db->execute($qry); // If no items left stop update if (!$rs) { break; } } 

?>

Вот хранимая процедура, которая выбирает случайную строку без использования ORDER BY RAND() и которая сбрасывает использованный флаг после того, как все элементы были использованы:

 DELIMITER // DROP PROCEDURE IF EXISTS random_iotd// CREATE PROCEDURE random_iotd() BEGIN # Reset used flag if all the rows have been used. SELECT COUNT(*) INTO @used FROM iotd WHERE used = 1; SELECT COUNT(*) INTO @rows FROM iotd; IF (@used = @rows) THEN UPDATE iotd SET used = 0; END IF; # Select a random number between 1 and the number of unused rows. SELECT FLOOR(RAND() * (@rows - @used)) INTO @rand; # Select the id of the row at position @rand. PREPARE stmt FROM 'SELECT id INTO @id FROM iotd WHERE used = 0 LIMIT ?,1'; EXECUTE stmt USING @rand; # Select the row where id = @id. PREPARE stmt FROM 'SELECT id, item FROM iotd WHERE id = ?'; EXECUTE stmt USING @id; # Update the row where id = @id. PREPARE stmt FROM 'UPDATE iotd SET used = 1 WHERE id = ?'; EXECUTE stmt USING @id; DEALLOCATE PREPARE stmt; END; // DELIMITER ; 

Использовать:

 CALL random_iotd(); 

Процедура предполагает структуру таблицы следующим образом:

 CREATE TABLE `iotd` ( `id` int(11) NOT NULL AUTO_INCREMENT, `item` varchar(255) NOT NULL, `used` BOOLEAN NOT NULL DEFAULT 0, INDEX `used` (`used`), PRIMARY KEY (`id`) ) ENGINE=InnoDB; 

Вот один из способов получить результат от PHP (чтобы все было просто, проверка ошибок была удалена):

 $mysqli = new mysqli('localhost', 'root', 'password', 'database'); $stmt = $mysqli->prepare('CALL random_iotd()'); $stmt->execute(); $stmt->bind_result($id, $item); $stmt->fetch(); echo "$id, $item\n"; // 4, Item 4 

UPADATE

Эта версия должна возвращать тот же результат повторно в заданную дату. У меня на самом деле не было времени, чтобы проверить это, поэтому не забудьте провести собственное тестирование …

 DELIMITER // DROP PROCEDURE IF EXISTS random_iotd// CREATE PROCEDURE random_iotd() BEGIN # Get today's item. SET @id := NULL; SELECT id INTO @id FROM iotd WHERE ts = CURRENT_DATE(); IF ISNULL(@id) THEN # Reset used flag if all the rows have been used. SELECT COUNT(*) INTO @used FROM iotd WHERE used = 1; SELECT COUNT(*) INTO @rows FROM iotd; IF (@used = @rows) THEN UPDATE iotd SET used = 0; END IF; # Select a random number between 1 and the number of unused rows. SELECT FLOOR(RAND() * (@rows - @used)) INTO @rand; # Select the id of the row at position @rand. PREPARE stmt FROM 'SELECT id INTO @id FROM iotd WHERE used = 0 LIMIT ?,1'; EXECUTE stmt USING @rand; # Update the row where id = @id. PREPARE stmt FROM 'UPDATE iotd SET used = 1, ts = CURRENT_DATE() WHERE id = ?'; EXECUTE stmt USING @id; END IF; # Select the row where id = @id. PREPARE stmt FROM 'SELECT id, item FROM iotd WHERE id = ?'; EXECUTE stmt USING @id; DEALLOCATE PREPARE stmt; END; // DELIMITER ; 

И структура таблицы:

 CREATE TABLE `iotd` ( `id` int(11) NOT NULL AUTO_INCREMENT, `item` varchar(255) NOT NULL, `used` BOOLEAN NOT NULL DEFAULT 0, `ts` DATE DEFAULT 0, INDEX `used` (`used`), INDEX `ts` (`ts`), PRIMARY KEY (`id`) ) ENGINE=InnoDB; 

Почему бы вам не использовать последовательность?

Последовательность служит вашей цели легко …