【问题标题】:PHP + MYSQL on Duplicate KEY still increase the INDEX KEYPHP + MYSQL on Duplicate KEY 还是增加INDEX KEY
【发布时间】:2015-08-14 19:56:23
【问题描述】:

我的桌子是这样的:

ID - EMAIL - VERSION - LASTUPDATE

其中 id = 是自动递增和 Primary:

并且电子邮件是唯一的

所以每次有人使用我的应用程序时,它都会获取用户电子邮件并尝试插入,如果电子邮件已经存在,它会将行更新为 $version 和 $lastupdate 的新值

它几乎 100% 工作,它已经完美地更新了行,问题是当它执行更新时,仍然增加自动增量 id,所以下一个 INSERT 不会是 lastID 加一,而是另一个数字示例:

ID - EMAIL - VERSION - LASTUPDATE
-----------------------------------
1 - test1@... - 1.0 - currentTime();

如果 test2 加载我的应用程序,它将添加并且数据库将是:

ID - EMAIL - VERSION - LASTUPDATE
-----------------------------------
1 - test1@.. - 1.0 - currentTime();
2 - test2@.. - 1.0 - currentTime();

现在想象一下,test2 有了新版本,并且更新到了这个:

ID - EMAIL - VERSION - LASTUPDATE
-----------------------------------
1 - test1@.. - 1.1 - currentTime();
2 - test2@.. - 1.0 - currentTime();

完美,不是吗?所以当test3打开我的应用程序时问题就开始了,id不会是3,而是4,因为更新给id增加了1(我不知道为什么)

ID - EMAIL - VERSION - LASTUPDATE
-----------------------------------
1 - test1@.. - 1.1 - currentTime();
2 - test2@.. - 1.0 - currentTime();
4 - test3@.. - 1.1 - currentTime();

为什么在更新时增加ID的加一?我不要它=(

我的sql语法:

$sql = "INSERT INTO `backups`.`regs` (`email`, `version`, `lastupdate`) VALUES ('$email', '$version', '$lastupdate')  ON DUPLICATE KEY UPDATE version='$version', lastupdate='$lastupdate'";

【问题讨论】:

  • 你可以通过使用事务来实现
  • @BranimirĐurek 有什么教程吗?我不知道如何
  • 这里是一个使用事务w3schools.com/php/func_mysqli_commit.asp的简单php示例。基本上,事务类似于 IF 状态。如果所有查询都成功执行,则提交更改,但如果您的查询之一失败,则什么也不做:)
  • 我很想看看创建表并对此进行测试。这很奇怪
  • 我现在记得这个问题并不奇怪,@BK435

标签: php mysql


【解决方案1】:

好的,现在我想起了这个问题。曾经有一个人想做插入,但如果你可以想象,每个插入必须以100 为增量,从@1000 开始。而且我们必须将整个东西包装在一个存储过程中才能有一个易受攻击的地方。你的问题浮出水面,它使他的编号偏离了 1 左右。

通过包装它,我们可以很明智地做到这一点,使用锁,并使用ALTER TABLE 维护 auto_inc 值

我对他说的另一种方法是创建一个增量表,锁定 1 行,获取该行中的值,使用它,将该 incTable 更新 100。解锁。

我们一直在嘲笑强迫症问题。我认为他只喜欢 10 的倍数,idk

编辑:

架构:

-- drop table ocd_nextnums;
create table ocd_nextnums
(   -- id table for nextnum, for the OCD impaired
    tableName varchar(100) not null,
    nextnum int not null
    -- won't bother with indexes, go for it if you want
)engine=INNODB; -- note engine type

insert ocd_nextnums(tableName,nextnum) values('thing',1);
insert ocd_nextnums(tableName,nextnum) values('some_other_table',1);

-- drop table thing;
create table thing
(   id int primary key, -- NOT an auto_increment, but is a PK
    email varchar(100) not null,
    version varchar(20) not null,
    lastupdate datetime not null,
    UNIQUE KEY (email)
)engine=MyIsam;

存储过程:

-- drop procedure putInThing;
delimiter $$
create procedure putInThing
(
    email_In varchar(100), version_In varchar(20)
)
BEGIN
    declare toUse int;
    declare theCount int;

    select count(*) into theCount from thing where email=email_In;
    select id into toUse from thing where email=email_In;   -- useful for result set @end
    IF theCount=1 THEN
        -- was there, do UPDATE
        update thing set version=version_In,lastupdate=now() where email=email_In;
    ELSE
        -- new row, do INSERT (please note the FOR UPDATE clause)
        select nextnum into toUse from ocd_nextnums where tableName='thing' FOR UPDATE;
        update ocd_nextnums set nextnum=nextnum+1 where tableName='thing';

        insert thing(id,email,version,lastupdate) values (toUse,email_In,version_In,now());
    end if;
    select toUse;   -- <------- that was your id
END
$$

测试:

call putInThing('t1@t.com','111');
call putInThing('t2@t.com','121');
call putInThing('t3@t.com','107');
select * from thing;
+----+----------+---------+---------------------+
| id | email    | version | lastupdate          |
+----+----------+---------+---------------------+
|  1 | t1@t.com | 111     | 2015-08-14 17:08:10 |
|  2 | t2@t.com | 121     | 2015-08-14 17:08:54 |
|  3 | t3@t.com | 107     | 2015-08-14 17:08:56 |
+----+----------+---------+---------------------+

call putInThing('t3@t.com','101111007'); -- is an update
call putInThing('t3@t.com','42'); -- is an update
call putInThing('t3@t.com','10007'); -- is an update
call putInThing('h@hillaryclinton.com','1'); -- is an insert

select * from thing;
+----+----------------------+---------+---------------------+
| id | email                | version | lastupdate          |
+----+----------------------+---------+---------------------+
|  1 | t1@t.com             | 111     | 2015-08-14 17:08:10 |
|  2 | t2@t.com             | 121     | 2015-08-14 17:08:54 |
|  3 | t3@t.com             | 10007   | 2015-08-14 17:22:09 |
|  4 | h@hillaryclinton.com | 1       | 2015-08-14 17:22:47 |
+----+----------------------+---------+---------------------+

来自Manual的Mysql INNODB部分:

要实现计数器的读取和递增,首先执行一个 使用 FOR UPDATE 锁定计数器的读取,然后递增 计数器.....

你会看到我使用这个吗,可能不会。只是展示一下。我对间隙和晚上睡觉很好。这就是为什么我将第一张桌子命名为我所做的:>

【讨论】:

  • 我收到了消息^^!所以最好的方法是在插入之前检查它是否存在,如果不插入,如果存在=更新行,不是吗?所以我避免了差距和强迫症问题=P
  • 标记为问题,因为它教会了我方法和我可能导致的问题,所以我宁愿先检查是否存在,如果不插入,如果存在更新行(我认为做这个更新超出 ON DUPLICATE 不会增加计数器,因为这是正常行为)thx Drew
  • 使用这个解决了 -> stackoverflow.com/questions/17465468/… 刚刚在 IF 中添加了 UPDATE ;) 再次感谢 Drew
  • 我的荣幸。不管你选择什么,希望它是安全的!
【解决方案2】:

根据您的描述,在您执行插入之前,您在 $version 中的值有误。这是在您没有向我们展示的代码中填充的。

您应该使用数据库中已经保存的版本号:

INSERT INTO `backups`.`regs` 
(`email`, `version`, `lastupdate`) 
VALUES ('$email', 1, '$lastupdate')  
ON DUPLICATE KEY 
UPDATE version=version+1, lastupdate='$lastupdate'"

【讨论】:

  • 我猜他误会了,我只是猜的
【解决方案3】:

因为你已经传递了值,那么你应该使用version = VALUES(version), lastupdate = VALUES(lastupdate) 代替ON DUPLICATE,试试这个:

$sql = "INSERT INTO `backups`.`regs` (`email`, `version`, `lastupdate`) VALUES ('$email', '$version', '$lastupdate')  ON DUPLICATE KEY UPDATE version = VALUES(version), lastupdate = VALUES(lastupdate)";

【讨论】:

  • 但我需要传递值,如果我不传递如何插入新记录?
  • 您应该将它们传递给插入,但不需要再次传递给on duplicate
  • @Drew 我测试了我的方法,我不知道,也许这种方法也可以。
  • 好的,我也会测试它,让你知道。好的,如果您使用 on duplicate key update; 完成语句,它会给出 1064 语法错误 ..... 如果这就是您的意思
  • 这并没有解决问题=(但是谢谢,它就像我以前一样工作
猜你喜欢
  • 2011-08-09
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多