views:

118

answers:

4

I have a table defined as follows:

CREATE TABLE  `mydb`.`users` (
  `userID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `userName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `createdDate` datetime NOT NULL,
  `active` tinyint(1) NOT NULL,
  `lastUpdatedDate` datetime NOT NULL,
  PRIMARY KEY (`userID`,`userName`) USING BTREE
)

However, when I run this query:

REPLACE INTO users SET userName ='Joe', active=1, lastUpdatedDate=now()

it inserts multiple rows with a userName Joe even though that is a primary key. I think because both userID and userName are primary keys, the key is only when both of them match. However, I can't remove the primary key on the userID as mysql throws an error. I want the userName to be distinct so there is only one record for each userName, and I want the replace statement to just update the lastUpdatedDate if the userName already exists (or insert it if it doesn't exist). How can I accomplish this?

+3  A: 

You are correct userID and userName together make up the primary key. Your replace into automatically creates the userID value thus creating valid unique keys

(1,joe)
(2,joe)
....

Issue this

ALTER TABLE `mydb`.`users` ADD CONSTRAINT UNIQUE uniqueusername (`userName`);

Which adds the wanted constraint on your table. Additionally to (userID,userName) being the primary key now the userName column by itself can only contain unique values.

You can use this query and get the wanted behaviour (it updates lastUpdatedDate and active when the userName exists and else inserts a new entry with lastUpdatedDate and createdDate set to now()).

INSERT INTO `users` (`userName`, `createdDate`, `active`, `lastUpdatedDate`)
VALUES ('Joe', now(), 1, now())
ON DUPLICATE KEY UPDATE `lastUpdatedDate` = now(), active = 1;
jitter
This works in that now I only have a single record for each userName, but when I run the REPLACE INTO command, the userID field is updated. I want that to stay the same it was when I first inserted the record and just have the lastUpdatedDate value change. Is that not possible?
David Buckley
Changed query to not change the userID and createdDate when users exists but only updates lastUpdatedDate and active. You should be able adopt this query to fit your specific needs
jitter
+1  A: 

You answered your own question, I think -- userName isn't "a" primary key, userName is part of the primary key. It seems like you want something like:

CREATE TABLE  `mydb`.`users` (
`userID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`createdDate` datetime NOT NULL,
`active` tinyint(1) NOT NULL,
`lastUpdatedDate` datetime NOT NULL,
PRIMARY KEY (`userID`) USING BTREE
UNIQUE KEY (`userName`)
)

(not syntax checked)

Maas
A: 

First, check your table for duplicate id's;

select userName
from users
group by userName
having count(*) > 1

Remove rows until there are no duplicates. Then, remove the existing primary key:

alter table users drop primary key

And recreate the primary key you'd like:

alter table users add primary key (userName)

This should now work, since there are no longer multiple rows with the same username.

Andomar
I deleted all data from the table, but when I run the first alter command to drop the primary key, I get this message: ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
David Buckley
I see per Tjofras' answer (http://stackoverflow.com/questions/1691371/mysql-able-to-enter-multiple-values-even-when-new-value-equals-an-existing-prim/1691430#1691430) that it will result in a new userID.
David Buckley
Andomar
You cannot drop the primary key because the AUTO_INCREMENT field has to be a KEY. If you really want the userName to be the PK you need to first add a regular KEY to the table by running: "alter table users add key (userID)"
Tjofras
Is a key just a synonym for index?
Andomar
+1  A: 
CREATE TABLE  `mydb`.`users` (
  `userID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `userName` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `createdDate` datetime NOT NULL,
  `active` tinyint(1) NOT NULL,
  `lastUpdatedDate` datetime NOT NULL,
  PRIMARY KEY (`userId`) USING BTREE,
  UNIQUE KEY (`userName`)
)

Something to be aware of is that REPLACE will actually remove the row and add a new one. This will result in a new userID (due to the AUTO_INCREMENT)

Tjofras
So is there no way to just update the lastUpdateddate without changing the userID?
David Buckley
Actually, there was an interesting blog post about what REPLACE really does.. maybe not what you think: http://jpipes.com/index.php?/archives/303-The-Deal-with-REPLACE-..-Or-Is-It-UPDATE.html
Maas