显示当天的项目

时间:2010-07-13 06:32:42

标签: php sql mysql random

我正在寻找创建一个从mySQL表中获取随机项的函数,但是让我保留返回的“当天项目”。换句话说,在所有其他项目都显示为当天项目之前,不应再次显示昨天“当天项目”的项目。

有关如何以优雅的方式执行此操作的任何建议吗?

由于

7 个答案:

答案 0 :(得分:4)

添加bool列“UsedAsItemOfTheDay”设置为false(0)。选择项目时更新为true。从拣配过程中排除已使用的项目。

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

(注意:这不是在MySql中返回随机行的最快方法;在巨大的表上它会很慢)

另请参阅:quick selection of a random row from a large table in mysql

答案 1 :(得分:1)

SELECT <fields> FROM <table> WHERE <some logic to exclude already used> ORDER BY RAND() LIMIT 1会从表格中随机获取一行。

答案 2 :(得分: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

答案 3 :(得分:1)

“知道”SQL的人会寻找声明性解决方案并避开程序代码。标记行是程序代码的“气味”。

Items的集合是静态的(永不改变)还是稳定的(很少改变)?如果是,那么从现在开始直到时间结束,一次性生成一个值的查找表会更容易,而不是将proc调度为每天运行以查找未使用的标志并更新今天的标志并清除所有标志如果已经使用过等等。

创建一个表示今天和表示应用程序生命周期的远期日期的连续日期表(当然,您可以考虑省略非工作日)。添加引用您Items表中的键的列(确保您选择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的集合发生变化,那么显然需要重新生成查找表,因此您需要平衡设计的简单性与手动维护的需要。

答案 4 :(得分:0)

如果您有固定的项目,可以添加列

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; }
}

&GT;

答案 5 :(得分:0)

这是一个存储过程,它选择一个随机行而不使用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;

答案 6 :(得分:-1)

为什么不使用序列?

顺序很容易实现你的目的......