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


PHP MySQL Insert Into & On Duplicate Key Problems

我的代码/语法有错吗?我不知道出了什么问题,而且我是新手。我在PHPMyAdmin中创建了一个表。它有两列。一个是"id",是主键/自动增量。另一列是"蒸汽名称"。

这个代码应该取一个人的网名并将其输入数据库。如果已经有一个记录,它应该以相同/最新的名称对其进行更新。

phpmyAdmin中表的名称是names

        <?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);
        ?>

我通过手动将PHPMyAdmin中"steamname"中的一行的值更改为"woogy"来测试这一点,然后运行此脚本来查看它是否会更新该数据库值。。。什么也没发生。它应该将"woogy"更新为正确的名称。

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

大家好。感谢您的意见。现在我的代码如下。如果还是错了,我很抱歉。我在学习。

            <?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(

点击此处图片

现在,当我运行PHP页面时,我的数据库会发生这种情况。

运行脚本后数据库的外观

不知道为什么它没有更新,也不知道为什么有这么大的空间?

INSERT ... ON DUPLICATE KEY的正确语法如下所示,您需要在VALUES中提到类似VALUES(column_name)而不是VALUES('$profile')的列名。此外,如果是UPDATE,则缺少PK列ID。因为您要为特定的ID值更新steamname

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

(或(

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

引用自MySQL文档

如果表包含AUTO_INCREMENT列和INSERT ... UPDATE插入一行,LAST_INSERT_ID()函数返回AUTO_INCREMENT值。如果该语句改为更新一行,LAST_INSERT_ID()没有意义。但是,你可以解决这个问题使用CCD_ 14。

除非尝试插入与现有主键列或唯一键列冲突的值,否则不能使用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)部分,这没有任何效果。这是一个禁忌@拉胡尔提出了这个建议,但不幸的是他或她错了。我将在以后的测试中去掉这个词。

那么,在这个INSERT中会发生什么呢?为steamname指定一个值,但不为ID指定值。因此MySQL为ID生成了一个新值。这将成为新行的主键值,并且该行将插入蒸汽名称"Woogy"。

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

接下来,我们尝试将名称更改为"Chatyak":

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

这将更改应用于哪一行?INSERT没有指定ID值,因此MySQL会自动增加另一个新的ID值。该值是新行的主键,这一行的蒸汽名称为"Chatyak"。

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

然后,如果您尝试插入与已存在的蒸汽名称相同的蒸汽名称,它将不会插入新行。它将更新现有行。

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)

请注意,上面写着该语句插入了"0行"。

但这仍然不允许您更改现有的蒸汽名称。如果指定一个新名称,它只会插入一个新行。如果指定了已在使用的名称,则该名称是重复的,因此不会插入新行,但也不会更改名称。

如果您让INSERT语句自动递增一个新的ID值,您希望它如何应用于现有行?您认为它应该与现有的哪一行冲突?


关于你的第二条评论:

您不需要第二个唯一密钥,我只是想向您展示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。这有变量吗?它是否对应于用户的会话数据?

******************编辑:*************************

回应@Rahul提供的答案。此声明:

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

在技术上是有效的,但无论发生什么,都将始终影响0行。事实上,它有很多错误,很难一一列举(这就是这次编辑的原因(。

本页底部MySQL文档中给出的ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)的用法是如何捕获已更新的行的id值的示例,即未插入,因为否则LAST_INSERT_ID()(无参数(将返回最后一个插入的id,在更新的情况下,这对于所讨论的行来说是没有意义的。

因此,如果上面的声明有效——尽管它不能——实际上与以下声明没有什么不同:

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

除了将该行的id值更新为原来的值之外。

除此之外,该语句永远无法工作的原因是,除非存在重复键,否则ON DUPLICATE KEY UPDATE子句永远不会被求值。当我们尝试将steamname更新为等于steamname的值时,也就是说,如果没有ON DUPLICATE KEY UPDATE子句,也会发生同样的错误,即最初触发ON DUPLICATE KEY UPDATE子句的值。

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

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

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

第三:由于将id设置为AUTO_INCREMENT,所以在未显式插入id值或使用WHERE子句指定id值的情况下插入到names表中的任何行都将创建一个具有新的自动递增id值的新行,无论您是否使用DUPLICATE KEY UPDATE,因为AUTO_INCREMENT将确保您永远不会生成重复密钥

我认为您可以使用mysql的REPLACE语句。这是最简单的查询,可以满足您所描述的需求。

REPLACE INTO `names` (`id`,`steamname`)
VALUES ($id, $steamname)

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

REPLACE语句将简单地DELETE,然后是INSERT,其中id值已经存在。但是,由于插入的是相同的id,因此效果是在包含匹配id值的行中更新steamname的值请注意,如果在同一个表中除了idsteamname之外还有其他列,并且您没有在REPLACE语句的VALUES中包含它们的值,那么这些值将丢失

第四:对于调试这类事情,了解实际传递到查询字符串中的值非常重要b(知道您正在使用的基本查询语法是正确的,除了您正在(或没有(使用的任何特定值。

最后:让事情变得简单;既然您已经在SimpleXMLElement中使用对象表示法来访问元素值,为什么不在mysqli中也使用它呢?试试这个。它是未测试,但它认为它会为您工作。

  <?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();
 ?>