By doing a `SHOW CREATE TABLE` on `wp_postmeta`, I got the structure of that table which included this:

     PRIMARY KEY (`meta_id`),

So the key that MySQL can do the 'insert or update' on is the meta_id, which you're not providing. Therefore in your query, it will always be added as a new row.

EDIT: The docs for `INSERT ON DUPLICATE UPDATE` say that it looks at either the PRIMARY KEY or any UNIQUE index, so you could possibly get around this by creating a new UNIQUE index on the combination of `post_id` and `meta_key` which might solve your problem. If this is a big table for you that could be a bad idea because it might slow down some read or write operations.

I'm not sure if this will work, and please be careful with database performance, but this is worth a go:

`ALTER TABLE 'wp_postmeta' ADD UNIQUE 'post_metakey_index' ('post_id', 'meta_key');`