質問

I'm trying to make a query that has the following behaviour:

  • Insert a new row if :id = NULL or a row where id = :id doesn't exist yet.
  • Update the an existing row if a row where id = :id already exists.
  • Set the value of activated to the NOW() if:
    • The current value of status <> :status. (aka. if the value of status will be changed)
    • The value of :status = 'active'. (aka. the new value of status will be 'active')

In order to do this, I have made the following prepared statement:

INSERT INTO test
  (`id`, `title`, `status`, `activated`)
VALUES
  (:id, :title, :status, NULL)
ON DUPLICATE KEY UPDATE
  id = LAST_INSERT_ID(id),
  status = VALUES(status),
  activated = IF(status <> VALUES(status) AND VALUES(status) = 'active', 
               NOW(), activated);

As far as I can see, this statement accurately describes the required logic. However, the value of activated always remains unchanged, regardless if the value of status changed to 'active' or not.

Can anyone explain what I am doing wrong? And (preferably) explain how to fix it?

役に立ちましたか?

解決

The problem is very un-SQL-like, you're assigning status before you compare it;

status=VALUES(status),                                 -- assign so they're equal
activated=IF(status <> VALUES(status) AND              -- compare, always equal
      VALUES(status) = 'active', NOW(), activated);

Just reverse the assignment and thing should work fine.

INSERT INTO test
  (`id`, `title`, `status`, `activated`)
VALUES
  (:id, :title, :status, NULL)
ON DUPLICATE KEY UPDATE
  id=LAST_INSERT_ID(id),
  activated=IF(status <> VALUES(status) AND 
               VALUES(status) = 'active', NOW(), activated),
  status=VALUES(status);

Btw, you only need to assign columns that actually change, no need to set id on update.

他のヒント

You don't specify the id (or key column) in your ON DUPLICATE KEY UPDATE clause.

From: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:

INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

What you currently have is most likely changing the last row you inserted into the database and not the duplicate key row. Read the docs on LAST_INSERT_ID(): http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id

Looking back at your question, I know I'm not really/fully answering your question. I'm partially trying to understand what the use case is. In cases where I'm creating a user through code, if it is the admin creating a user, I just set the account active (and set the activated column if there is one). If it is a user creating an account through a web form, they get set as pending until they use the link in their email. The link in the email uses an activation code that allows me to do something like:

update user set status='active' activated=NOW() where code=:activation_code
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top