INSERT if not exists UPDATE if exists does not work - php

I am trying to have the solution of the very well known INSERT IF NOT EXISTS UPDATE IF EXISTS.
But mine is not working. I don't know why, Can anyone figure it out?
Here is what I have tried yet:
$qprep = ("INSERT INTO gpsdata (`imei`,`latitude`,`longitude`)
VALUES ('$imei','$lathex1','$lonhex1') ON DUPLICATE KEY UPDATE
latitude='$lathex1',longitude='$lonhex1';");
I want to update the row if the same "imei" is in there, or Insert if its not.
I have my ROW as the primary key and from phpmyadmin, I have made the imei "unique".
What am I doing wrong?
My SQL DUMP:
CREATE TABLE IF NOT EXISTS `gpsdata` (
`ROW` int(11) NOT NULL AUTO_INCREMENT,
`IMEI` varchar(255) NOT NULL,
`Latitude` varchar(255) NOT NULL,
`Longitude` varchar(255) NOT NULL,
PRIMARY KEY (`ROW`),
UNIQUE KEY `IMEI` (`IMEI`,`Latitude`,`Longitude`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=36 ;
--
-- Dumping data for table `gpsdata`
--
INSERT INTO `gpsdata` (`ROW`, `IMEI`, `Latitude`, `Longitude`) VALUES
(24, '#2:359672050035420:2:*', '90.370803333333', '0'),
(30, '#2:359672050035420:2:*', '90.370803333333', '23.7584'),
(27, '#2:359672050035420:2:*', '90.370803333333', '23.75854'),
(35, '1:135790246811221:1:*', '1.0961283333333', '1.759595'),
(32, '1:135790246811221:1:*', '1.759595', '1.0961283333333');

As seen here, you need to replace the actual values in the update statement with either A| references to the alreay existing values (e.g. longitude=longitude) or B| references to the new values (e.g. longitude=VALUES(longitude), but not longitude='$lonhex1').
Your query should be rewritten:
$qprep = ("INSERT INTO gpsdata (`imei`,`latitude`,`longitude`)
VALUES ('$imei','$lathex1','$lonhex1') ON DUPLICATE KEY UPDATE
latitude=VALUES(latitude),longitude=VALUES(longitude)");

If you have statement based replication running on this server then there would be a problem, see the warning below:
Unsafe statement written TO the BINARY LOG USING statement FORMAT
since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE
ON a TABLE WITH more THAN ONE UNIQUE KEY IS unsafe

You have to pass the column name and its value on which you have used the primary key or unique key on which you want the Duplicate Key Update.
If it gets the id(in your case ROW, latitude and longitude column on which primary and unique key is defined ) in the database, it updates it, else it inserts a new row.
$qprep = ("INSERT INTO gpsdata (`imei`,`latitude`,`longitude`)
VALUES ('$imei','$lathex1','$lonhex1') ON DUPLICATE KEY UPDATE
latitude=VALUES(latitude),longitude=VALUES(longitude)");
Example:
INSERT INTO gpsdata (`row`,`imei`,`latitude`,`longitude`)
VALUES ('24','1','TEST','TEST') ON DUPLICATE KEY UPDATE
`IMEI`='2', `Latitude`='2',`Longitude`='2';
or
INSERT INTO gpsdata (`imei`,`latitude`,`longitude`)
VALUES ('1','TEST','TEST') ON DUPLICATE KEY UPDATE
`IMEI`='2', `Latitude`=VALUES(`Latitude`),`Longitude`=VALUES(`Longitude);

Related

Can i get the `insert_id` (actually just the `id`) when inserting with `ON DUPLICATE KEY`? [duplicate]

Supposing I have a table:
CREATE TABLE files (
id_prod INT UNSIGNED NOT NULL DEFAULT PRIMARY KEY AUTO_INCREMENT,
id_rel INT UNSIGNED,
name VARCHAR(250),
other VARCHAR(200),
UNIQUE INDEX(id_rel , name)
);
and I want to use an unique query to insert/update the data on this table:
INSERT INTO files (id_rel , name)
VALUES ('25', 'test')
ON DUPLICATE KEY UPDATE
now, reading the MySQL manual I read about this:
ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)
so I thought my query should be:
INSERT INTO files (id_rel , name)
VALUES ('25', 'test')
ON DUPLICATE KEY UPDATE id_prod = LAST_INSERT_ID(id), name = 'TESTED'
but which is the difference if I use only:
INSERT INTO files (id_rel , name)
VALUES ('25', 'test')
ON DUPLICATE KEY UPDATE name = 'TESTED'
?
I cannot understand the meaning of LAST_INSERT_ID(id). What is (id) and what it's supposed to do?
This is only necessary if your application needs to call LAST_INSERT_ID() after performing the INSERT. Normally, LAST_INSERT_ID() will only return a value if you actually inserted a new row into the table, not of there was a duplicate key and it updated the row instead.
From the documentation:
If expr is given as an argument to LAST_INSERT_ID(), the value of the argument is returned by the function and is remembered as the next value to be returned by LAST_INSERT_ID().
If you use the idiom you quoted, LAST_INSERT_ID() will return either the ID of the new row that was inserted or the row that was updated.

Can't insert : A foreign key constraint fails

I have the following tables:
CREATE TABLE IF NOT EXISTS `location`(
`ID` int(11) NOT NULL,
`name` varchar(25) NOT NULL,
`water` varchar(25) NOT NULL,
`fodder` varchar(25) NOT NULL,
`access` varchar(25) NOT NULL,
PRIMARY KEY (`ID`)
KEY `water` (`water`)
KEY `fodder` (`fodder`)
KEY `access` (`access`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `watercondition`(
`ID` int(11) NOT NULL,
`watervalue` varchar(25) NOT NULL,
PRIMARY KEY (`watervalue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `foddercondition`(
`ID` int(11) NOT NULL,
`foddervalue` varchar(25) NOT NULL,
PRIMARY KEY (`foddervalue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `accesscondition`(
`ID` int(11) NOT NULL,
`accessvalue` varchar(25) NOT NULL,
PRIMARY KEY (`accessvalue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
And for constraints table :
ALTER TABLE `location`
ADD CONSTRAINT `location_ibfk2` FOREIGN KEY (`water`) REFERENCES `watercondition` (`watervalue`),
ADD CONSTRAINT `location_ibfk3` FOREIGN KEY (`fodder`) REFERENCES `foddercondition` (`foddervalue`),
ADD CONSTRAINT `location_ibfk4` FOREIGN KEY (`access`) REFERENCES `accesscondition` (`accessvalue`);
In my php file, i want to insert a value to all of the table like this :
$sqlwater = "INSERT INTO `watercondition` (`ID`, `watervalue`) VALUES ('".$_SESSION['loc_id']."', '$watervalue')";
$resultwater = mysqli_query($con, $sqlwater) or die (mysqli_error($con));
$sqlfodder = "INSERT INTO `foddercondition` (`ID`, `foddervalue`) VALUES ('".$_SESSION['loc_id']."', '$foddervalue')";
$resultfodder = mysqli_query($con, $sqlfodder) or die (mysqli_error($con));
$sqlaccess = "INSERT INTO `accesscondition` (`ID`, `accessvalue`) VALUES ('".$_SESSION['loc_id']."', '$accessvalue')";
$resultaccess = mysqli_query($con, $access) or die (mysqli_error($con));
$sqlloc = "INSERT INTO `location` (`ID`, `name`) VALUES ('".$_SESSION['loc_id']."', '$name')";
$resultaccess = mysqli_query($con, $access) or die (mysqli_error($con));
But when I execute the php file, I get this error :
Cannot add or update a child row: a foreign key constraint fails (mydb.location, CONSTRAINT location_ibfk2 FOREIGN KEY (water) REFERENCES watercondition (watervalue))
When I check on my db, the value from water, fodder, and access have already been inserted db, but not in my location table.
The insert into the location table must also include values for the water, fodder and location columns. They are columns in the location table and cannot just be ignored.
Also you were using the wrong query variable in the final query.
I guess what is happening here is that the constraints are being validated by MYSQL before it checks that you have values for all the NOT NULL fields, so you get the constraint error before the more obvious one about missing column values.
$sqlloc = "INSERT INTO `location`
(`ID`, `name`, `water`, `fodder`, `location`)
VALUES ('{$_SESSION['loc_id']}', '$name',
'$watervalue', '$foddervalue', '$accessvalue' )";
$resultaccess = mysqli_query($con, $sqlloc) or die (mysqli_error($con));
You should insert foreign key to your location table to maintain relation you have created before.
Your query for location should be like
`$sqlloc = "INSERT INTO 'location' ('ID', 'name', 'water', 'footer', 'access') VALUES ('".$_SESSION['loc_id']."', '$name', 'water-related-id', 'fodder-related-id, 'access-related-id)";`
Anyway, primary key for a table should be 'id' column and foreign key should be other table's primary key, so your constraints should be
ALTER TABLE 'location'
ADD CONSTRAINT 'location_ibfk2' FOREIGN KEY ('water') REFERENCES 'watercondition' ('id'),
ADD CONSTRAINT 'location_ibfk3' FOREIGN KEY ('fodder') REFERENCES 'foddercondition' ('id'),
ADD CONSTRAINT 'location_ibfk4' FOREIGN KEY ('access') REFERENCES 'accesscondition' ('id');
And, your foreign key data type should match referenced table's key which in your case is int(11).
Are you doing this for college assignment?
Change your last insert statement like below:
"INSERT INTO `location` (`ID`, `name`,`water`,`fodder`,`access`) VALUES
('".$_SESSION['loc_id']."', '$name','$watervalue','$foddervalue','$accessvalue')";
Explanation:
ERD:
Look the yellow marked tables are the parent tables and your location table is child table.
So according to MySQL you cannot add or update a child table row (location) by some foreign key values which don't exist in corresponding parent table(s).
In your first three insert statements you are updating your three parent tables which is fine.
But in your final insert statement you are trying to update your child table where you provide with no values for those foreign keys which is an exception.
SQL FIDDLE
Reference
Twothings to note here:
First of all, your insert is not working because you made all the foreign keys "NOT NULL" fields, so when you insert a record on the location table you must provide those values.
Second, you must make sure the fields that are foreign keys has the same data type on the original table. In your code you are using INT(11) id fields on the original tables but are using VARCHAR(25) on the location table and you are linking to the field holding the value instead of linking to the primary key (id) field of the referenced table.
Kind regards,
Daniel

MySQLi does not return error code

In my MySQL database I have a table "table1" with unique constraint set on column "name" - I want to prevent duplicate names.
If there's already name 'John' in table this code:
$db=new mysqli(...);
$sql="INSERT INTO table1 SET id=10,name='John'";
if(!$db->query($sql))
{
if($db->errno==1062)
{
throw new InsertNonUniqueException(...);
}
else
{
throw new InsertException(...);
}
}
should throw InsertNonUniqueException() (my own exception). Instead, it throws InsertException().
Execution of query returns false and execution enters the if() loop. Also $db->row_affected is -1 but problem is that $db->errno is always O (it should be 1062)!!! So I can't detect that my insert error was caused by violating unique key constraint on name column!
I don't know why mysqli does not return 1062 code when unique key constraint violation occurs!
I can't leave a comment, thus going to ask you here.
Please provide the result of SHOW CREATE TABLE table1;
I can't reproduce your problem using your code and next table:
CREATE TABLE `table1` (
`name` varchar(11) COLLATE utf8_unicode_ci NOT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Am I the only one around here that thinks you have an error in your SQL syntax?.. There is no room for SET in INSERT INTO, because you can only use SET in UPDATE statements (assuming you habe MySQL in version 5.5 or below).
INSERT INTO syntax is like the following (as described in the docs):
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]
OR
INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
Try it like this:
<?php
$sql="INSERT INTO table1 (id, name) VALUES ('10', 'John')";
...
step 1
make sure that the table has a unique key
SHOW CREATE TABLE table1
expected result
CREATE TABLE `table1` (
`id` INT(11) default NULL,
`name` varchar(11) COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci
if there is UNIQUE KEY name (name) we have a unique key
step 2
try to change your code
$db = new mysqli(...);
// first insert
if( !$db->query("INSERT INTO table1 (id, name) VALUES (10, 'John')") ) {
throw new Exception($db->error);
}
// second insert (for me raise: Duplicate entry 'John' for key 'name')
if( !$db->query("INSERT INTO table1 (id, name) VALUES (11, 'John')") ) {
throw new Exception($db->error);
}
Please, try these two steps
Side note: if you have name and id as duplicates, only the first duplicate encountered will be returned in the message.
The only issue i have with your code is that:
having setup your table and columns.
I setup a unique index on the table. I did .. stuff on a two column table that ensure it works.
You missed the 'new'
keyword when you 'throw exceptions'.
this is the only error with your posted code that i could find.
i.e: throw new Exception('Division by zero.'); // example taken from PHP manual.

query takes 70 ms to execute

I have an MySQL table named i_visited structured like: userid,tid,dateline
And I run this condition in view_thread.php page:
if (db('count','SELECT userid FROM i_visited
WHERE tid = '.intval($_GET['id']).'
AND userid = '.$user['id']))
mysql_query('UPDATE i_visited
SET dateline = unix_timestamp(now())
WHERE userid = '.$user['id'].'
AND tid = '.intval($_GET['id']));
else
mysql_query('INSERT INTO i_visited (userid,tid,dateline) VALUES
('.$user['id'].','.intval($_GET['id']).',unix_timestamp(now()))');
The problem is that it executes in 80/100 ms (on Windows) 40/60 (on Linux)
1 row affected. (query executed in 0.0707 sec)
The mysql_num_rows() aka db('count',sql) uses 2 / 3 ms, so the problem is at the update and the insert.
P.S. i_visited is an utf8_unicode_ci (InnoDB), has anyone seen this problem?
Other queries run normal (2 / 3 milliseconds)
CREATE TABLE i_visited (
userid int(10) NOT NULL,
tid int(10) unsigned NOT NULL,
dateline int(10) NOT NULL,
KEY userid (userid,tid),
KEY userid_2 (userid),
KEY tid (tid) )
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
You do not need to do a select to check existence and then choose either Update or Insert.
You can use MySQL's ON DUPLICATE KEY UPDATE Feature like this.
$query = 'INSERT INTO
i_visited (userid,tid,dateline)
VALUES (' .
$user['id'] . ',' .
intval($_GET['id']) . ',
unix_timestamp(now()))
ON DUPLICATE KEY UPDATE
dateline = unix_timestamp(now())';
mysql_query($query);
This query will insert a new row if there is now KEY conflict, and in case a duplicate key is being inserted, it will instead execute the update part.
And as you have a KEY userid (userid,tid) in your CREATE Statement the above query is equivalent to your if...else block.
Try this and see if there are any gains
You can also use REPLACE INTO, as there are only the specified 3 columns, like this
$query = 'REPLACE INTO
i_visited (userid,tid,dateline)
VALUES (' .
$user['id'] . ',' .
intval($_GET['id']) . ',
unix_timestamp(now()))';
mysql_query($query);
But I would suggest looking at ON DUPLICATE KEY UPDATE as it is more flexible, as it can be used on a table with any number of columns, whereas REPLACE INTO would only work in some limited cases as other column values would also need to be filled in the REPLACE INTO statement unnecessarily
I think (part) of the problem is that your table does not have an explicit primary key.
You've only declared secondary keys.
Change the definition to:
CREATE TABLE i_visited (
userid int(10) NOT NULL,
tid int(10) unsigned NOT NULL,
dateline int(10) NOT NULL,
PRIMARY KEY userid (userid,tid), <<----------
KEY userid_2 (userid),
KEY tid (tid) )
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
InnoDB does not work well without an explicit primary key defined.

PHP: use id to another column

Ok, so i have a normal query that inserts to the database.
mysql_query("INSERT INTO users_pm_in (uID, msg) VALUES ('$uID', '$msg')");
Now this table has also a column called "id" with auto_increment & primary key.
When it inserts it auto makes number for the column in the row. Now I want this number, and put it in column dialog, in the same row. So the inserted row have the same number/id in "id" and "dialog". How can i do that?
Not sure if this can be done in one query (or why you even want to do this), but you can use this:
mysql_query("INSERT INTO users_pm_in (uID, msg) VALUES ('$uID', '$msg')");
mysql_query("UPDATE users_pm_in SET dialog = id WHERE id = '".mysql_insert_id()."');
Be sure to escape the variables properly also.
I think it would be easier to remove the autoincrement and add the id+dialog value yourself.
Check out mysql_insert_id()
You can do this, altough it's not very efficient...
Supose you have this table:
CREATE TABLE `test` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`a` INT(10) NULL DEFAULT '0',
`b` INT(10) NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
You can perform the following query:
INSERT INTO test (a, b) SElECT IFNULL((MAX(id) +1),1), 200 FROM test;
Notice that "200" is some random value that will be inserted on "b" column.

Categories