Getting ON DUPLICATE KEY UPDATE working - php

I have a form and a database table named reports. I have a date field (primary key) and a textarea named changes to say what's been changed on that date. If the date is the same I want to be able to overwrite the information in the 'changes' column for that date.
My insert command, which works on its own, is as follows:
mysql_query("
INSERT INTO reports (thedate,changes)
VALUES ('$_POST[thedate]','$_POST[changes]')
");
I understand that I'll need to use ON DUPLICATE KEY UPDATE after my INSERT but after numerous attempts I cannot get it right. Not only do things no update but it seems to break my insert command so even a new row isn't added to the database.
Apologies if this is a duplicate question. After lots of searching and lots of trying I cannot get it to work.

Have a look att REPLACE.
REPLACE works exactly like INSERT, except that if an old row in the
table has the same value as a new row for a PRIMARY KEY or a UNIQUE
index, the old row is deleted before the new row is inserted.
Note that if you have a foreign key with an action ON DELETE it will be triggered when using REPLACE since it does a delete followed by an insert .
Using ON DUPLICATE KEY it could look like this:
INSERT reports (thedate, changes) VALUES ('$_POST[thedate]', '$_POST[changes]')
ON DUPLICATE KEY UPDATE changes = '$_POST[changes]'

This is the plain SQL query:
INSERT INTO reports (thedate, changes)
VALUES ('2011-11-10', 'Lorem ipsum')
ON DUPLICATE KEY UPDATE changes=VALUES(changes)
http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html
Now, you absolutely need to sit down and try to understand what SQL is and how it interacts with PHP and differs from it. You are using PHP to compose strings that happen to be code from another language called SQL. The way you are doing it, the resulting code can be valid SQL or not, and it'll depend of the arbitrary data sent by any anonymous visitor. In the best case, your script will crash. In the worse case, the visitor will be able to read confidential data or alter your database. Here's the example in the manual page for mysql_query():
// This could be supplied by a user, for example
$firstname = 'fred';
$lastname = 'fox';
// Formulate Query
// This is the best way to perform an SQL query
// For more examples, see mysql_real_escape_string()
$query = sprintf("SELECT firstname, lastname, address, age FROM friends
WHERE firstname='%s' AND lastname='%s'",
mysql_real_escape_string($firstname),
mysql_real_escape_string($lastname));
// Perform Query
$result = mysql_query($query);

This should work:
mysql_query("INSERT INTO reports (thedate,changes) VALUES ('$_POST[thedate]','$_POST[changes]') ON DUPLICATE KEY UPDATE changes=VALUES(changes)");
Disadvantage of using REPLACE is that it's not standard SQL but a MySQL extension. Beside that, when using auto incremented columns, REPLACE will reinsert with a different value. I won't recommend it to anyone.
Edit: ON DUPLICATE KEY isn't standard SQL either. Sorry!

Related

MySQL Insert Unique Value

I have a MySQL Table that has a column that will AUTO_INCREMENT, another to store user-submitted data, and another column that defaults to CURRENT_TIMESTAMP.
My INSERT query is:
$query = $db->prepare("INSERT IGNORE INTO `UserData` (`user_data`) VALUES(?)");
$query->bind_param('s', $commentdata);
$query->execute();
However it is still inserting the duplicate values (if a user clicks submit multiple times). What is the best way to prevent this?
MySQL will not create duplicate auto increment ids (unless you have a very badly configured cluster) so presumably the duplicates you refer to are in a different attribute - you've only told us about user_data.
If you don't want duplicates in there then add a unique index on the column. You should also add error handling to deal with failures when the situation arises and remove the 'IGNORE'.
However you also need to think about your controlling logic (this hints that you probably have csrf vulnerabilities) and your user interface (why are you allowing users to submit the same form twice?)
I ended up using the following query:
INSERT INTO `UserData` (`post_num`, `user_data`)
SELECT ?, ? FROM `UserData`
WHERE NOT EXISTS (
SELECT * FROM `UserData` WHERE `post_num`= ? AND `user_data`=?
) LIMIT 1
Then I do $query->bind_param('isis', $post_number, $comment, $post_number, $comment); to bind the values.
This will check to make sure there are no duplicate comments on a post.
Note that my actual example inserts more information such as the user information and I check to make sure there are no duplicate comments from that certain user on a specific post.
The best way to prevent duplicate values in a MySQL table is for the table definition to use the UNIQUE or PRIMARY constraint. See the documentation for table creation syntax.
You may also want the value to be a KEY if you plan on performing lookups using that value.

Avoid Duplicates of Unique Key Within INSERT Query

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.

Searching for duplicate entries with PDO

I'm having a spot of trouble with a bit of code meant to find duplicates of a name along with the platform. This will also be adapted to find unique IDs later on.
So for example, if there is a server named "Apple" on the Xbox and you try to insert a record with the name "Apple" with the same platform it will reject it. However, another platform with the same name is allowed, such as "Apple" with PS3.
I've tried coming up with ideas and searching for answers, but I'm kind of in the dark as to what is the best way to go about checking for duplicates.
So far this is what I have:
$nameDuplicate_sql = $db->prepare("SELECT * FROM `servers` WHERE name=':name' AND platform=':platform'");
$nameDuplicate_sql->bindValue(':name', $name);
$nameDuplicate_sql->bindValue(':platform', $platform);
$nameDuplicate_sql->execute();
I've tried a bunch of different solutions, some from here, others from the PHP's manual and etc. None appear to work though.
I'm trying to stick with PDO, however, this is one instance where I cannot figure out where to turn. If this was in mysql_* I probably could just use mysql_affected_rows, but with PDO I have no clue. rowCount seemed promising, but it always returns 0 since this is neither an INSERT, UPDATE, or DELETE statement.
Oh, and I've tried the SQL statement in phpMyAdmin and it works; I tried it with a simple name/platform and it found rows properly.
If anyone can help me out here I'd appreciate it.
For most databases, PDOStatement::rowCount() does not return the
number of rows affected by a SELECT statement.
Instead, use PDO::query() to issue a SELECT COUNT(*) statement with the same predicates as your intended SELECT statement, then use
PDOStatement::fetchColumn() to retrieve the number of rows that will
be returned.
Your application can then perform the correct action.
Instead of checking for duplicates, why not just enforce it on the database table directly? Create a composite key that will prohibit entries being made if they are already there?
CREATE TABLE servers (
serverName varchar(50),
platform varchar(50),
PRIMARY KEY (serverName, platform)
)
This way, you will never get duplicates, and it also allows you to use the mysql insert... on duplicate key update... syntax which sounds like it might be rather handy for you.
If you already have a Primary Key on it or you don't want to make a new table, you can use the following:
ALTER TABLE servers DROP PRIMARY KEY, ADD PRIMARY KEY(serverName, platform);
Edit: A primary key is either a single row or a number of rows that have to have unique data in them. A single row cannot have the same value twice, but a composite key (which is what I am suggesting here) means that between the two columns, the same data cannot appear.
In this case, what you want to do, add in a server name and have it associated with a platform - the table will let you add in as many rows containing the same server name - as long as each one has a unique platform associated with it - and vice versa, you can have a platform listed as many times as you like, as long as all the server names are unique.
If you try to insert a record where the same servername/platform combination exists, the database simply won't let you do it. There is another golden benefit though. Due to this key constraint - mysql allows a special type of query to be used. It is the insert... on duplicate key update syntax. That means if you try to insert the same data twice (ie, database says no) you can catch it and update the row you already have in the table. For example:
You have a row with serverName=Fluffeh and it is on platform=Boosh but you don't know about it right now, so you try to insert a record with the intention of updating the server IP address.
Normally you would simply write something like this:
insert into servers (serverName, platform, IPAddress)
values ('$serverName', '$platform', '$IPAddy')
But with a nice primary key identified you can do this:
insert into servers (serverName, platform, IPAddress)
values ('$serverName', '$platform', '$IPAddy')
on duplicate key update set IPAddress='$IPAddy';
The second query will insert the row with all the data if it doesn't exist already. If it doesm, Bam! it will update the IP Address of the server which was your intention all along.
Remove the single quotes from your query on the parameter tokens... they will be quoted once they are bound... thats part of the reason for a prepared statement.
$nameDuplicate_sql = $db->prepare("SELECT * FROM `servers` WHERE name= :name AND platform= :platform");

MySQL database entry duplication

I have a PHP script search script that logs every query made in a MySQL database starting with 1 in the value column. Currently, if the same terms are searched more than once, 1 is added to the number in the value column. However, after two searches for the same word, the query gets added to the database again. Why could this be?
My PHP code is:
<?php
$database=mysql_connect("localhost","username","password");
mysql_select_db("database",$database);
$query=$_GET['q'];
logQuery($query);
function logQuery($query){
$query="insert into queries (query) values ('$query') on duplicate key update value=value+1";
mysql_query($query);
}
?>
Sounds like either query is not your primary key, or that $query is different from the DB contents. My money is on the key; could it be a composite key? i.e. id AND query?
In this situation/example, using mysql_escape_string(trim($query)) is a MUST to avoid SQL injection.
If the query is getting added a second time, then it is obviously not a primary key, which prevents the 'on duplicate key' from working
Do you define query field as unique? maybe a whitespace or something, try using md5 as unique key

MySQL only insert if there isn't an exact row in the Table

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.

Categories