For My user table in MySQL, before entering new userid & avoid duplicate userid, I check if similar Id exist. To me there are 3 approach :
Query for new userid (SELECT ...) and check 0 row returned. If row exists then request for new userid, else (INSERT...)
Make userid column UNIQUE in user table and directly INSERT... if 0 rows affected then request new userid
Make userid column UNIQUE in user table and directly INSERT... if mysqli_errno ($link)==1062 then request new userid
I presently use 1st method but it results in 2 query and intend to switch to second method. I found 3rd approach in a book which confused me !
My question is
Is 2nd approach better for duplicate entry prevention ?
Are 2nd & 3rd aprroach same or different ?
Is there still any better approach to prevent duplicate entry with minimum DB Query ?
Here are my answers:
Is 2nd approach better for duplicate entry prevention?
yes, in my opinion second approach is better, if you know that userid is going to be always unique, you should make it a unique field, MySQL will automatically handle it.
Are 2nd & 3rd approach same or different?
I think both are same and MySQL throws error in these cases.
Is there still any better approach to prevent duplicate entry with minimum DB Query?
You can put UNIQUE INDEX on this column, that will be faster.
1) Duplication should be handled by your database by make unique fields.
2) When you try to insert the same data in a unique field, it'll give you an error. The error would be like -
ERROR 1062 (23000): Duplicate entry '2147483647' for key 'UNIQUE'
3) Hence to avoid such error, you have to check through your code that is the data already available in the table.
CONCLUSION:
You have to use 1 & 2 approaches parallely to be 100% ensure that, no duplicate entries would exist in your table.
Related
This may be more of a design/logic type question, but what I'm attempting to do is poll a web service which returns about 15 "records" of foo. I then take those 15 records and attempt a SQL INSERT with them. If I poll again and get back 20 records, I attempt an INSERT only on the new 5 records.
My problem is on the first page load, when it performs the first poll, it will always return all the records. When I do the initial INSERT, of course I will typically get a bunch of "Violation of PRIMARY KEY constraint" errors. This is currently "by design", but I'm wondering if there's a better approach, or if this is an ok approach. Something about filling up the php error log repeatedly tells me it's not. :)
Is this resolved simply with a SQL-side Try/Catch?
Schema below:
If you want to avoid causing errors, you can check which of the posts already exists with an SQL statment like this
SELECT id FROM table WHERE id IN ([id list])
where id is your primary key column, table the table (surprise) and [id list] a comma separated list of the id´s you want to insert. Use the PHP function implode (documentation) to create one if you have the id´s in a list.
Then just exclude the rows with the returned id´s.
I have a table that looks like (irrelevant columns subtracted):
PRIMARY KEY(AUTO-INCREMENT,INT),
CLIENTID(INT),
CLIENTENTRYID(INT),
COUNT1(INT),
COUNT2(INT)
Now, the CLIENTID and CLIENTENTRYID is a unique combined index serving as a duplication prevention.
I use PHP post input to the server. My query looks like:
$stmt = $sql->prepare('INSERT INTO table (COUNT1,COUNT2,CLIENTID,CLIENTENTRYID) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE COUNT1=VALUES(COUNT1),COUNT2=VALUES(COUNT2)');
$stmt->bind_param("iiii",$value,$value,$clientid,$cliententryid);
The SQL object has auto commit enabled. The "value" variable is reused as the value in COUNT1 and COUNT2 should ALWAYS be the same.
Okay - that works fine, most of the time, but randomly, and I cannot figure out why, it will post 0 in COUNT2 - for an entirely different row.
Any ideas how that might occur? I can't see a pattern (it doesn't happen after a failed attempt, which is why the unique index exists, so that a new attempt will not cause duplicates). It seems to be completely random.
Is there something I've misunderstood about ON DUPLICATE KEY UPDATE? The VERY weird thing is that it updates A DIFFERENT row incorrectly - not the one you insert.
I realize other factors might affect this, but now I'm trying to rule out my SQL logic as a source of error.
Aside from the PRIMARY KEY on the auto_increment column, there is only ONE UNIQUE key defined the table, and that's defined on (CLIENTID,CLIENTENTRYID), right?
And there are no triggers defined on the table, right?
And you are (obviously) using a prepared statement with bind placeholders.
It doesn't really matter if those two columns (CLIENTID and CLIENTENTRYID) are defined as NOT NULL or not; MySQL will allow multiple rows with NULL values; that doesn't violated the "uniqueness" enforced by a UNIQUE constraint. (This the same as how Oracle treats "uniqueness" of NULL values, but it is different from how SQL Server enforces it.)
I just don't see any way that the statement you show, that is:
INSERT INTO `mytable` (COUNT1,COUNT2,CLIENTID,CLIENTENTRYID) VALUES (?,?,?,?)
ON DUPLICATE KEY
UPDATE COUNT1 = VALUES(COUNT1)
, COUNT2 = VALUES(COUNT2)
... theres no way that Would cause some other row in the table to be updated.
Either the insert action succeeds, or it throws a "duplicate key" exception. If the "duplicate key" exception is thrown, the statement catches that, and performs the UPDATE action.
Given that (CLIENTID,CLIENTENTRYID) is the only unique key on the table (apart from the auto_increment column, not referenced by this statement), the update action will be equivalent to this statement:
UPDATE `mytable`
SET COUNT1 = ?
, COUNT2 = ?
WHERE CLIENTID = ?
AND CLIENTENTRYID = ?
... using the values supplied in the VALUES clause of the INSERT statement.
Bottom line, there isn't an issue in anything OP showed us. The logic is sound. There is something else going on, apart from this SQL statement.
OP code shows as using scalars (and not array elements) as arguments in the bind_param call, so that whole messiness of passing by reference shouldn't be an issue.
There's not an issue with the SQL statement OP has shown, based on everything OP told us and shown us. The issue reported has to be something other than the SQL statement.
Looking at the MySQL doc, it says that given an insert statement
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
if column a and b are unique, the insert is equivalent to an update statement with a WHERE clause containing an OR instead of an AND:
UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;
And to quote from the documentation,
If a=1 OR b=2 matches several rows, only one row is updated. In
general, you should try to avoid using an ON DUPLICATE KEY UPDATE
clause on tables with multiple unique indexes.
Hope this helps.
UPDATE:
As per further discussion, OP will consider re-visiting existing database design. OP also has another table with similar multiple unique index spec, but without the same problem by utilizing INSERT IGNORE.
I found the answer.
As everyone here correctly suggested, this was something else. For some completely bizarre reason, the button I used to open the "add new entry" somehow POST'ed to set arrived = 0 on a selected object in a table view that has nothing to do with the button.
This must have been a UI linking somewhere in my Storyboard.
I'm sorry I wasted so much of your time guys. At least I learned a little more about SQL and indexes.
i think problem is with your are using values in UPDATE COUNT1=VALUES(COUNT1),COUNT2=VALUES(COUNT2) try to use like this
ON DUPLICATE KEY UPDATE COUNT1 = $v1,COUNT2 = $v2;
I have a MySQL query that looks like this:
INSERT INTO beer(name, type, alcohol_by_volume, description, image_url) VALUES('{$name}', {$type}, '{$alcohol_by_volume}', '{$description}', '{$image_url}')
The only problem is that name is a unique value, which means if I ever run into duplicates, I get an error like this:
Error storing beer data: Duplicate entry 'Hocus Pocus' for key 2
Is there a way to ensure that the SQL query does not attempt to add a unique value that already exists without running a SELECT query for the entire database?
You could of course use INSERT IGNORE INTO, like this:
INSERT IGNORE INTO beer(name, type, alcohol_by_volume, description, image_url) VALUES('{$name}', {$type}, '{$alcohol_by_volume}', '{$description}', '{$image_url}')
You could use ON DUPLICATE KEY as well, but if you just don't want to add a row INSERT IGNORE INTO is a better choice. ON DUPLICATE KEY is better suited if you want to do something more specific when there are a duplicate.
If you decide to use ON DUPLICATE KEY - avoid using this clause on tables with multiple unique indexes. If you have a table with multiple unique indexes ON DUPLICATE KEY-clause could be giving unexpected results (You really don't have 100% control what's going to happen)
Example: - this row below only updates ONE row (if type is 1 and alcohol_by_volume 1 (and both columns are unique indexes))
ON DUPLICATE KEY UPDATE beer SET type=3 WHERE type=1 or alcohol_by_volume=1
To sum it up:
ON DUPLICATE KEY just does the work without warnings or errors when there are duplicates.
INSERT IGNORE INTO throws a warning when there are duplicates, but besides from that just ignore to insert the duplicate into the database.
As it just so happens, there is a way in MySQL by using ON DUPLICATE KEY UPDATE. This is available since MySQL 4.1
INSERT INTO beer(name, type, alcohol_by_volume, description, image_url)
VALUES('{$name}', {$type}, '{$alcohol_by_volume}', '{$description}',
'{$image_url}')
ON DUPLICATE KEY UPDATE type=type;
You could also use INSERT IGNORE INTO... as an alternative, but the statement would still throw a warning (albeit, instead of an error).
Yes, there is. You can use the ON DUPLICATE KEY clause of mysql INSERT statement. The syntax is explained here
INSERT INTO beer(name, type, alcohol_by_volume, ...)
VALUES('{$name}', {$type}, '{$alcohol_by_volume}', ...)
ON DUPLICATE KEY UPDATE
type={$type}, alcohol_by_volume = '{$alcohol_by_volume}', ... ;
Yes, by first selecting the name from the database, and if the result of the query is not null (zero records), then the name already exists, and you have to get another name.
Quite simply - your code needs to figure out what it wants to do if something's trying to insert a duplicate name. As such, what you need to do first is run a select statement:
SELECT * FROM beer WHERE name='{$name}'
And then run an 'if' statement off of that to determine if you got a result.
if results = 0, then go ahead and run your insert.
Else ... whatever you want to do. Throw an error back to the user? Modify the database in a different way? Completely ignore it? How is this insert statement coming about? A mass update from a file? User input from a web page?
The way you're reaching this insert statement, and how it should affect your work flow, should determine exactly how you're handling that 'else'. But you should definitely handle it.
But just make sure that the select and insert statements are in a transaction together so that other folks coming in to do the same sort of stuff isn't an issue.
I'm using PHP/MySQL. What is the best way to check if the username is already taken?
Right now all I do is execute a select statement to see if the username is already taken. If the select returns a row then I stop and display an error message. If the select doesn't return anything then I insert the new user.
But is there a more efficient way? For example can I use UNIQUE on the username column and then only execute an insert statement(and get an error from the insert if it's already taken).
You do have the risk that some other thread will insert your user name in the brief moment between your SELECT confirming that the user doesn't exist and the INSERT where you insert it. This is called a race condition.
It may seem like the chance of that happening is slight, but there's a saying about that: "One in a million is next Tuesday."
Yes, a UNIQUE constraint on the username can prevent you from INSERTing a duplicate username. Declare that constraint.
Some articles will tell you that once you have a UNIQUE constraint, you don't have to do the SELECT anymore. Just try the INSERT and if it conflicts, then report the error.
However, I helped a site recover from a problem caused by this technique. On their site, users were attempting to create new usernames very quickly, and resulting in a lot of conflicts on the INSERT. The problem was that MySQL increments the auto-increment primary key for the table, even if the INSERT was canceled. And these auto-increment values are never reused. So the result was that they were losing 1000-1500 id numbers for every INSERT that succeeded. They called me when their INT primary key reached 231-1 and they couldn't create another row in the table!
The solution they used was first to try the SELECT, and report a duplicate. Then if it seemed safe, try the INSERT -- but write the code to handle the possible conflict with the UNIQUE constraint anyway, because the race condition can still occur.
I need to insert this in a table but only if there isn't a replica of the row already. (both values should be equal). How can I change the code to work this way? Thanks
<?php
mysql_select_db("cyberworlddb", $con);
mysql_query("INSERT INTO Badges (UID, Website)
VALUES ('1', 'www.taringa.net')");
mysql_close($con)
?>
You could create a single index for the UID and Website columns and make that index unique, then use INSERT IGNORE. The result will be that if it is a duplicate, it will just be ignored.
If you need to be able to tell if the SQL inserted a row, then follow it up with a call to mysql_affected_rows() which should return 0 if it didn't do anything and 1 if it inserted the record.
Easiest thing to do is use INSERT IGNORE and have a unique key on the fields. It will insert if no row exists, otherwise do nothing.
What about a unique index on (UID, Website), which would cause the insert to fail?
First up, about the question. It is simple bad to check for "an exact" replica of row in RDBMS. That is just too costly. The right question to ask is what makes my row unique and what is the minimum I can get away with. Putting in unique constraints on big columns is a bad idea.
Answers saying that you should include UID in unique constraint are again just BAD. UID is most likely a generated key and the only input coming from outside is website name. So the only sane thing to do here is to put a unique constraint on website column.
Then the insert code should handle unique constraint errors coming out from the database. You can get the error number from DB handle, like
$errorNo = $mysql->errno ;
Then check for a particular code (1062 in case of MYSQL) that corresponds to unique key violation.