有效地存储每个项目的最后X个记录

时间:2010-04-06 08:49:58

标签: mysql performance

我希望以有效的方式将最后的X条记录存储在MySQL数据库中。因此,当存储第4条记录时,应删除第1条记录。

我这样做的方式不是首先运行获取项目的查询。然后检查我应该做什么,然后插入/删除。

必须有更好的方法来做到这一点。有什么建议吗?

  

修改

我想我应该补充说,存储的记录没有唯一的编号。他们有一个混合的标准杆。例如article_id和user_id。

然后我想创建一个包含user_x的最后X项的表。

只选择按用户分组并按时间排序的表中的文章对我来说不是一个选项。我进行排序和分组的表有数百万条记录,并且无缘无故遭受重创。因此,在最后的X记录之间创建一个表更方便。

PS。我没有将它用于文章和用户。

3 个答案:

答案 0 :(得分:1)

在存储过程中实现它(表名为ibt,代表表之间):

delimiter ;
DROP TABLE IF EXISTS `ibt`;
CREATE TABLE `ibt` (
  `seqid` int(10) unsigned NOT NULL auto_increment,
  `article_id` varchar(10) NOT NULL default '',
  `user_id` varchar(10) NOT NULL default '',  
   anotherVar VARCHAR(10),
  PRIMARY KEY  (`article_id`,`user_id`),
  KEY `seqid` (`seqid`)
) ENGINE=MEMORY AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;


drop procedure if exists addEntry;
delimiter $$
create procedure addEntry(_article_id INT, _user_id INT, _anotherVar VARCHAR(10))
begin
  DECLARE done INT DEFAULT 0;
  declare seq INT;    
  declare seqNew INT DEFAULT 1;  
  declare Cnt INT DEFAULT 0;  

  declare cur CURSOR for
      SELECT seqid
      from ibt 
      where user_id=_user_id   
      order by seqid desc;  
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

  START TRANSACTION;

  open cur;  
  REPEAT  
    FETCH cur INTO seq;    
    IF NOT done THEN        
      SET Cnt = Cnt+1;       
      IF Cnt = 3 THEN            
         DELETE FROM `ibt` where seqid = seq;
      END IF;      
      IF Cnt = 1 THEN            
         SET seqNew = seq + 1;
      END IF;      
    END IF;
  UNTIL done END REPEAT;

  INSERT into `ibt` 
  SET article_id=_article_id, 
        user_id=_user_id,  
      seqid=seqNew,      
      anotherVar=_anotherVar;

  close cur;  

  COMMIT;
end $$

delimiter ;

call addEntry(1, 1, 'a');
call addEntry(2, 1, 'b');
call addEntry(3, 1, 'c');
call addEntry(4, 1, 'd');

您可以将上述SQL作为一个单元进行测试。我用过HeidiSQL。

在数据库中存储过程后,可以从PHP代码中“调用addEntry”。

答案 1 :(得分:0)

将前三项的主键保存在文件或其他持久存储中,然后执行更新语句而不是删除/插入?

如果你想将所有数据放入数据库,那么我会在中间表中的所有记录中添加一个唯一的序列号(让我们称之为seqid),然后仍然执行查询但不是获取整行,而只是得到序列号,例如

SELECT seqid from inbetweentable where article_id=? and user_id=?

以您的编程语言(比如PHP)命令,然后进行更新

UPDATE inbetweentable SET seqid=BIGGESTID+1, ... WHERE seqid=SMALLESTID 

(BIGGESTID和SMALLESTID来自您的PHP代码)

修改 要仅从SQL语句中返回一个值(逗号分隔的String):

 SELECT GROUP_CONCAT(seqid) as idsCsv from inbetweentable where article_id=? and user_id=? ORDER BY seqid

并在PHP中解析它。这将节省mysql和PHP端的循环代码,应该更快。 e.g。

<?php
// Get single row
... 
$seqIds = explode(',', $row['idsCsv']);

答案 2 :(得分:0)

我认为您应该在交易中执行以下操作:

  1. 插入新记录
  2. 获取多余记录的ID
  3. 使用步骤2中的ID删除记录(如果有)
  4. 如果您可以在单个查询中合并步骤2和3,那将会很棒,但这似乎不可能,因为您需要对同一个表进行排序和删除,这是不允许的。

    以下是一些提示:

    CREATE TEMPORARY TABLE items (
      item_id int unsigned not null auto_increment,
      data_ varchar(5) not null,
      primary key(item_id)
    );
    
    INSERT INTO items (data_)
      VALUES ('data1'), ('data2'), ('data3'), ('data4'), ('data5'), ('data6');
    
    # select ids of the excess items
    SELECT item_id
      FROM items, (select @cnt:=0) as cnt
      WHERE IF((@cnt:=@cnt+1)<=3, 0, 1)
      ORDER BY item_id DESC;
    

    最后一个查询将返回:

    +-------+
    |item_id|
    +-------+
    |   3   |
    |   2   |
    |   1   |
    +-------+