PHP MySQL插入&关于重复的关键问题

时间:2014-07-27 20:06:38

标签: php mysql on-duplicate-key

我的代码/语法错了吗?我不确定出了什么问题,对此我不熟悉。我在PHPMyAdmin中创建了一个表。它有两列。一个是" id"并且是主键/自动增量。另一列是" steamname"。

此代码应该采用某人的在线名称并将其输入数据库。如果已有记录,则应使用相同/最新名称进行更新。

phpmyAdmin中表格的名称是名称

        <?php
        // Capture person's name from XML file
        $profile = simplexml_load_file('UrlGoesHere.Com/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA);
        echo (string)$profile->steamID;

        //Enter name into databse and overwrite it if same/duplicate
        $con=mysqli_connect("localhost","username","password","databaseName");
        // Check connection
        if (mysqli_connect_errno())
        {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
        }

        // Perform queries
        mysqli_query($con,"INSERT INTO names (steamname) VALUES ('$profile') ON   DUPLICATE KEY UPDATE steamname = VALUES('$profile')");
        mysqli_close($con);
        ?>

我通过手动更改&#34; steamname&#34;中行的值来测试了这一点。在PHPMyAdmin到&#34; woogy&#34;然后运行此脚本以查看它是否会更新该数据库值...没有发生任何事情。它应该更新&#34; woogy&#34;正确的名称。

----------- ---------------更新

大家好。谢谢你的反馈。我现在的代码如下。对不起,如果还是错的话。我正在学习。

            <?php
        $profile = simplexml_load_file('http://steamcommunity.com/profiles/76561198006938281/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA);
        echo (string)$profile->steamID;


        $con=mysqli_connect("localhost","userHere","passwordHERE","DBName");
        // Check connection
        if (mysqli_connect_errno())
        {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
        }

        // Perform queries
        mysqli_query($con,"INSERT INTO names (steamname) VALUES ('$profile') ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)");
        mysqli_close($con);
        ?>

这是数据库在运行脚本之前的样子:( woogy应该改为Chatyak)

Click image here

现在,当我运行PHP页面时,这就是我的数据库。

How database looks after running script

不确定为什么它没有更新 - 以及为什么会有这么大的空间?

3 个答案:

答案 0 :(得分:2)

INSERT ... ON DUPLICATE KEY的正确语法如下所示,您需要在VALUES中提及VALUES(column_name)而非VALUES('$profile')的列名称。此外,如果ID,您错过了PK列UPDATE。因为您要更新特定steamname值的ID

INSERT INTO names (steamname) 
VALUES ('$profile') 
ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)

(OR)

INSERT INTO names (steamname) 
VALUES ('$profile') 
ON DUPLICATE KEY UPDATE steamname = VALUES(steamname)

引自MySQL documentation

  

如果表格包含AUTO_INCREMENT列且INSERT ... UPDATE   插入一行,LAST_INSERT_ID()函数返回   AUTO_INCREMENT值。LAST_INSERT_ID()如果语句更新了一行,   LAST_INSERT_ID(expr)没有意义。但是,你可以解决这个问题   使用{{1}}。

答案 1 :(得分:1)

除非您尝试插入与现有主键或唯一键列冲突的值,否则不能使用INSERT ON DUPLICATE KEY UPDATE(IODKU)来更新而不是插入新行。

由于您未在此INSERT中指定ID的任何值,因此它将始终递增自动增量主键并插入新行。

如果希望INSERT替换现有行的steamname,则必须在INSERT中指定ID值。


重新发表您的第一条评论:

我看了你的样品。它创造了一个新行并不奇怪。它生成了一个新的自动增量ID值,因为您没有在INSERT中指定ID。所以它自然地创造了一个新的行。它无法告诉您要替换哪一行。

这是一个演示:

mysql> create table names (id serial primary key, steamname text);

mysql> insert into names (steamname) values ('Woogy') 
  on duplicate key update ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname);

忽略ID = LAST_INSERT_ID(ID)部分,这没有任何效果。这是一个无操作。 @Rahul建议,但不幸的是,他或她错了。我将在后续测试中取出该术语。

那么这个INSERT会发生什么?您为steamname指定了值,但ID没有值。所以MySQL为ID生成一个新值。这将成为新行的主键值,并使用steamname&#39; Woogy&#39;插入该行。

mysql> select * from names;
+----+-----------+
| id | steamname |
+----+-----------+
|  1 | Woogy     |
+----+-----------+

接下来,我们尝试将名称更改为&#39; Chatyak&#39;:

mysql> insert into names (steamname) values ('Chatyak') 
  on duplicate key update steamname = VALUES(steamname);

这会将更改应用于哪一行? INSERT未指定ID值,因此MySQL会自动增加另一个新ID值。该值是新行的主键,而且是获取steamname&#39; Chatyak&#39;的行。

mysql> select * from names;
+----+-----------+
| id | steamname |
+----+-----------+
|  1 | Woogy     |
|  2 | Chatyak   |
+----+-----------+

这意味着如果每次INSERT时让自动增量生成新的ID值,就永远不会触发ON DUPLICATE KEY部分。每次INSERT都会产生一个新行。

如上所述,除非您尝试插入与表中已存在的行的主键或唯一键冲突的值,否则ON DUPLICATE KEY不会执行任何操作。但在这种情况下,您不会发生冲突,您始终会生成新的ID值。所以它插入一个新行。

如果你对steamname有一个UNIQUE KEY约束,那么这将是另一个触发ON DUPLICATE KEY的机会。 但它仍然不会做你想做的事。

mysql> alter table names add unique key (steamname(20));

然后,如果您尝试将相同的 steamname插入已存在的那个,它将不会插入新行。它会更新现有的行。

mysql> insert into names (steamname) values ('Chatyak') 
  on duplicate key update ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname);
Query OK, 0 rows affected (0.02 sec)

注意它说&#34; 0行&#34;被该声明插入。

但是这仍然不允许您更改现有的steamname。如果指定新名称,则只需插入新行。如果您指定的名称已经在使用中,则它会重复,因此无法插入新行,但也不会更改名称。

如果允许INSERT语句自动递增新的ID值,您如何期望INSERT语句应用于现有行?您认为哪个现有行应与之冲突?


重新发表您的第二条评论:

您不需要辅助唯一密钥,我只想告诉您IODKU的工作原理。

在考虑SQL语法之前,您必须考虑问题的逻辑。换句话说,如果您指定一个新名称,您希望它替换哪个现有行?

例如:

mysql> insert into names (id, steamname) values (123, 'Chatyak') 
  on duplicate key update steamname = VALUES(steamname);

这样可行,因为如果存在ID为123的行,则此INSERT将导致重复键冲突,因此将触发ON DUPLICATE KEY子句。但是在这个例子中我们从哪里获得了值123?大概在您的应用程序中,您知道您认为自己正在更新的ID。有变量吗?它是否与用户的会话数据相对应?

答案 2 :(得分:1)

<强> **************************编辑:**************** *********

回应@Rahul提供的答案。这句话:

INSERT INTO names (steamname) 
VALUES ('$profile') 
ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)

在技术上是有效的,但无论如何都会完全影响0行。实际上它有很多错误,很难枚举它们(这就是编辑的原因)。

this page底部的MySQL文档中提供的ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)的使用是如何捕获已更新的行的id值的示例未插入,因为否则LAST_INSERT_ID()(没有参数)会返回最后一个插入 ID,如果是更新< / strong>对于有问题的行而言没有意义。

所以上述陈述,如果它曾经有效 - 虽然它不能 - 与这句话实际上没有什么不同:

INSERT INTO names (steamname) 
VALUES ('$profile') 
ON DUPLICATE KEY UPDATE steamname = VALUES(steamname)

除了您还要将该行的id值更新为已经存在的值。

除此之外,语句永远不会起作用的原因是因为除非存在重复键,否则永远不会评估ON DUPLICATE KEY UPDATE子句。如果我们尝试将ON DUPLICATE KEY UPDATE更新为等于steamname的值,即与steamname相同的值,则会发生没有ON DUPLICATE KEY UPDATE子句时发生的相同错误首先是条款。

*****************************编辑结束************ ***********************

首先:当您尝试在查询中使用$profile时,SimpleXMLElement仍然是SimpleXMLElement::__toString对象的实例。因此调用了SimpleXMLElement::__toString方法,但<steamname>方法仅从第一个元素中直接返回文本内容,而不返回任何后代的文本。因此,如果您在<steamID>调用返回的父元素中嵌套了一些元素simplexml_load_file和一些元素VALUES,则不会返回它们的值。请参阅manual

第二:当UPDATE函数与DUPLICATE KEY UPDATE colname = VALUES(some_col_name)子句一起使用时,正确的语法是引用列名,如DUPLICATE KEY UPDATE colname = 'value'。如果要使用显式值,则语法为id。请参阅manual

第三:由于您已将AUTO_INCREMENT设置为names,因此在id表中插入任何行而未明确插入WHERE值或使用id指定id值的子句将创建一个新行,其中包含新的自动递增DUPLICATE KEY UPDATE值,无论您是否使用AUTO_INCREMENT,因为 REPLACE将确保您永远不会生成重复的密钥

我认为您可以使用mysql的REPLACE INTO `names` (`id`,`steamname`) VALUES ($id, $steamname) 语句代替。这是最简单的查询,适用于您描述的需求。

$id

其中$profile->steamID$steamname,而steamname是该行REPLACE列的新(或非新)值。

DELETE语句只会INSERT,然后id已存在id值。但是,由于您要插入相同的steamname,因此效果是更新包含匹配id值的行中id的值。 请注意,如果您在同一个表格中除steamnameREPLACE之外还有其他列,并且您未在VALUES语句中包含其值 <?php // Capture person's name and id from XML file $profile = simplexml_load_file('UrlGoesHere.Com/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA); $profile_id = (string)$profile->steamID; $profile_name = (string)$profile->steamName; //connect to database $mysqli = new mysqli("localhost","username","password","databaseName"); // Check connection if (mysqli_connect_errno()) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); } //build query //IMPROTANT: this will delete other fields(if any) //> in the row, unless they are also refilled by this statement $q = (" REPLACE INTO `names` (`id`,`steamname`) VALUES (?, ?) "); //uncomment to debug query //echo "<pre>$q</pre>"; //uncomment to debug values //echo "<pre>profile_name: \n $profile_name</pre>"; //echo "<pre>profile_id: \n $profile_id</pre>"; //prepare statement using above query $stmt = $mysqli->prepare($q); //fill in '?'s with actual values //first argument is the data types //'i' [integer] 's' [string] //follownig aruments fill in '?'s in order $stmt->bind_param('is', $profle_id, $profile_name); // execute statement $stmt->execute(); //close statement $stmt->close(); //close connection $mysqli->close(); ?> 然后这些价值将会丢失。

第四:为了解决这些对 a 重要的事情,知道你实际传递给查询字符串的值是什么。 b )知道您使用的基本查询语法是正确的,除了您(或不是)使用它的任何特定值。

最后:让自己轻松自如;因为你已经使用SimpleXMLElement的对象表示法来访问元素值,为什么不将它与mysqli一起使用呢?试试这个。它是 UNTESTED ,但它认为它对您有用。

{{1}}