Php 检查唯一键并更新行或创建新的行WordPress

Php 检查唯一键并更新行或创建新的行WordPress,php,mysql,wordpress,Php,Mysql,Wordpress,我有一个可拖动的div,其中位置被保存到数据库中,用户id设置为unique。如何检查用户id是否存在。。如果有,则更新其他3列。。如果不存在,请插入新行。我读过使用“复制密钥更新”的文章,但很难实现它。有什么想法吗 如@N.B.-工作解决方案所述 global $wpdb; $_POST['user_id']; $_POST['div_id']; $_POST['x_pos']; $_POST['y_pos']; $user_id = $_PO

我有一个可拖动的div,其中位置被保存到数据库中,用户id设置为unique。如何检查用户id是否存在。。如果有,则更新其他3列。。如果不存在,请插入新行。我读过使用“复制密钥更新”的文章,但很难实现它。有什么想法吗

如@N.B.-工作解决方案所述

global $wpdb;

    $_POST['user_id'];
    $_POST['div_id'];
    $_POST['x_pos'];
    $_POST['y_pos'];

    $user_id    = $_POST['user_id'];
    $div_id     = $_POST['div_id'];
    $x_pos      = $_POST['x_pos'];
    $y_pos      = $_POST['y_pos'];

$wpdb->query($wpdb->prepare(" INSERT INTO coords

   (user_id, div_id, x_pos, y_pos)
    VALUES (%d, %s, %d, %d)
    ON DUPLICATE KEY UPDATE
    x_pos = VALUES(x_pos), y_pos = VALUES(y_pos)",

    $user_id,
    $div_id,
    $x_pos,
    $y_pos
    ));

正如@N.B.在评论中指出的,虽然我提交的第一种方法有效,但它对比赛条件是开放的。我要感谢他指出这一点。这是第一个具有竞争条件的解决方案:

$user_exists = $wpdb->get_var(
    $wpdb->prepare("SELECT user_id FROM coords WHERE user_id = %d", $user_id)
);
if($user_exists) {
    // Do Update
}
else {
    // Do Insert
}
这些竞争条件是天文数字,因为插入必须在第一个查询返回和下一个插入查询之间的时间内完成执行。但竞争条件仍然存在,因此可能在某个时间点发生

但是,您的数据库仍然是安全的。当它发生时,它不会复制数据,而是会抛出一个wpdb错误,即一个唯一的密钥已经存在,插入将以静默方式失败(它不会终止脚本,也不会输出错误,除非打开错误报告)

令人惊讶的是,Wordpress核心和无数插件和主题作者都使用了上述技术,我只能在Wordpress核心中找到两个正确使用“ON DUPLICATE”的实例。所以,互联网上有很多这样的比赛情况,只是为了让你了解我们正在谈论的天文数字的机会

不管有没有机会,使用它都是不好的做法。正如N.B.评论的那样,数据库应该关注数据,而不是PHP

真正的解决办法 无论出于何种原因,Wordpress没有“重复更新时插入”功能,这意味着您必须每次使用$wpdb->query编写一个查询,或者构建自己的函数来处理它。我之所以选择编写函数,是因为每次编写wpdb->query都是一件痛苦的事情,它会让用户更接近意外的mysql注入。还有发展速度

/**
 * Insert on Duplicate Key Update.
 *
 * Wordpress does not have an 'insert on duplicate key update' function, which
 * forces user's to create their own or write standard queries. As writing
 * queries is annoying and open to mysql injection via human error, this function
 * automates this custom query in an indentical fashion to the core wpdb functions.
 * Source: http://stackoverflow.com/a/31150317/4248167
 *
 * @global wpdb $wpdb
 * @param string $table The table you wish to update.
 * @param array $data The row you wish to update or insert, using a field => value associative array.
 * @param array $where The unique keys that you want to update at or have inserted, also a field => value array.
 * @param array $data_formats Wordpress formatting array for the data. Will default to %s for every field.
 * @param array $where_formats Wordpress formatting array for the where. Will default to %s for every field.
 * @return boolean True if successfully inserted or updated the row.
 */
function insertOrUpdate($table, $data, $where, $data_formats = array(), $where_formats = array())
{
    if(!empty($data) && !empty($where)) {
        global $wpdb;
        // Data Formats - making sure they match up with the data.
        $default_data_format = (isset($data_formats[0])) ? $data_formats[0] : '%s';
        $data_formats = array_pad($data_formats, count($data), $default_data_format);
        $data_formats = array_splice($data_formats, 0, count($data));
        // Where Formats - making sure they match up with the where data.
        $default_where_format = (isset($where_formats[0])) ? $where_formats[0] : '%s';
        $where_formats = array_pad($where_formats, count($where), $default_where_format);
        $where_formats = array_splice($where_formats, 0, count($where));
        // Get Fields
        $data_fields = array_keys($data);
        $where_fields = array_keys($where);
        // Create Query
        $query =
            "INSERT INTO $table" .
            " (" . implode(', ', array_merge($data_fields, $where_fields)) . ")" .
            " VALUES(" . implode(', ', array_merge($data_formats, $where_formats)) . ")" .
            " ON DUPLICATE KEY UPDATE";
        // Compile update fields and add to query
        $field_strings = array();
        foreach($data_fields as $index => $data_field) {
            $field_strings[] = " $data_field = " . $data_formats[$index];
        }
        $query .= implode(', ', $field_strings);
        // Put it all together - returns true on successful update or insert 
        // and false on failure or if the row already matches the data.
        return !!$wpdb->query(
            $wpdb->prepare(
                $query,
                array_merge(
                    array_merge(
                        array_values($data),
                        array_values($where)
                    ),
                    array_values($data)
                )
            )
        );
    }
    return false;
}
要使用它,只需输入参数,就像在$wpdb->update函数调用中一样

insertOrUpdate(
    'testing_table',
    array('column_a' => 'hello', 'column_b' => 'world'),
    array('id' => 3),
    array('%s', '%s'),
    array('%d')
);
在您的情况下,它将是:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id),
    array('%d', '%d', '%d'),
    array('%d')
);
也可以只使用默认格式:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id),
    array('%d')
);
或者,您可以忽略默认为字符串格式的格式:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id)
);

如果您发现任何问题,请告诉我。

更新的格式不正确-请查看我的帖子,我已经更新了它。它错过了这个机会,没问题。很高兴我能帮忙。:)因为这种方法是不正确的,并且容易并发,从而产生误报。唯一性是通过设置唯一键、插入数据并在插入失败时捕获异常来“检查”的。选择和检查行数是不正确的,当您收到响应时,另一个进程可以插入用户,这样您就可以得到两个相同的行数。PHP中从不检查唯一性。我们有数据库是有原因的,他们担心数据。好吧,那你回答这个问题怎么样?你的解释很好,但不能回答问题。如果你这样做,我会很高兴地删除我的答案。你如何根据我的评论修改答案,并在重复密钥更新中加入
示例,我给你一个向上投票?我已经编辑了上面的代码(适用于可能有相同问题的其他用户)要反映@True建议的工作代码,您应该在重复密钥更新时使用
。您接受的答案不正确。@N.B.我试图实现该方法,但没有成功。如果你说下面的答案不起作用(事实上是这样),那么你能展示一个使用ON DUPLICATE KEY的有效示例吗?下面提供的答案实际上是有效的。是的,它有效,因为您正在作为唯一的用户对它进行测试。如果同时完成两个请求,会发生什么情况?双方都会得出结论,没有这样的用户,双方都会插入数据——没有什么可以停止复制数据。这就是为什么我们使用唯一键,这就是为什么我们让数据库担心数据的唯一性,而不是PHP。若您并没有正确地测试它,并且它并没有被正确地测试,那个么这个方法就是有效的。说重复键上的
不起作用是没有帮助的,您必须指定它不起作用的方式(SQL语法错误、不更新、空白页等)。@N.B.-是的,它起作用了。。我已经添加了上述代码的其余部分。“用户id”列设置为“唯一”。。。
insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id)
);