Using On Duplicate Key Update with an array - php

I'm relatively new to MYSQL and am having trouble combining idea I have read about. I have a form generated from a query. I want to be able to insert or update depending on whether there is currently a matching row. I have the following code which works for inserting but I;m struggling with the On DUPLICATE UPDATE part I keep getting a message saying there is an error in my syntax or unexpeted ON depending on how I put the ' .
require_once("connect_db.php");
$row_data = array();
foreach($_POST['attendancerecordid'] as $row=>$attendancerecordid) {
$attendancerecordid=mysqli_real_escape_string($dbc,$attendancerecordid);
$employeeid=mysqli_real_escape_string($dbc,($_POST['employeeid'][$row]));
$linemanagerid=mysqli_real_escape_string($dbc,($_POST['linemanagerid'][$row]));
$abscencecode=mysqli_real_escape_string($dbc,($_POST['abscencecode'][$row]));
$date=mysqli_real_escape_string($dbc,($_POST['date'][$row]));
$row_data[] = "('$attendancerecordid', '$employeeid', '$linemanagerid', '$abscencecode', '$date')";
}
if (!empty($row_data)) {
$sql = 'INSERT INTO attendance (attendancerecord, employeeid, linemanagerid, abscencecode, date) VALUES '.implode(',', $row_data)
ON DUPLICATE KEY UPDATE abscencecode = $row_data[abscencecode];
echo $sql;
$result = mysqli_query ($dbc, $sql) or die(mysqli_error ($dbc));
}
The various echo statements are showing that the correct data is coming through and my select statement was as expected before I added in the ON DUPLICATE statement.

You need to fix the way the sql statement is constructed via string concatenation. When you create an sql statement, echo it and run it in your favourite mysql manager app for testing.
$sql = 'INSERT INTO attendance (attendancerecord, employeeid, linemanagerid, abscencecode, date) VALUES ('.implode(',', $row_data).') ON DUPLICATE KEY UPDATE abscencecode = 1'; //1 is a fixed value yiu choose
UPDATE: Just noticed that your $row_data array does not have named keys, it just contains the entire new rows values as string. Since you do bulk insert (multiple rows inserted in 1 statement), you have to provide a single absencecode in the on duplicate key clause, or you have to execute each row in a separate insert to get the absence code for each row in a loop.

Related

INSERT INTO SELECT with VALUES in one query

Bit new to MYSQL and PHP - I have the following code that will create a new record in multiple tables that I have setup however I need to merge the following code together somehow as it creates separate rows instead of one record
$sql .=("INSERT INTO orders (customer_id) SELECT customer_id FROM
customer_details;");
foreach($result as $item){
$mysql_desc = $item['product_description'];
$mysql_mode = $item['delivery_mode'];
$mysql_cost = $item['course_cost'];
$sql .=("INSERT INTO orders(product_description, delivery_mode, course_cost) VALUES('$mysql_desc', '$mysql_mode', '$mysql_cost');");
}
The result I'm getting:
Based on your data I assume that you want to insert the customer id and the values from php into the same record. In this case you need to combine them into the same insert ... select ... statement:
$sql .=("INSERT INTO orders(customer_id, product_description, delivery_mode, course_cost) select customer_id, '$mysql_desc', '$mysql_mode', '$mysql_cost' from customer_details;");
Couple of things to note:
This insert ... select ... statement will insert the same records for all customers in the customer details table. I'm not sure if this is your ultimate goal.
Pls consider the advices made in the comments regarding the old mysql API and the use of prepared statements.
To put this more into what I would expect to happen, something along the lines of - prepare statement, then loop through each item and add in new row...
$insert = $connection->prepare("INSERT INTO orders (customer_id,product_description, delivery_mode, course_cost)
VALUES (?,?,?,?)");
foreach($result as $item){
$customerID = 1; // Have to ensure this is what your after
$mysql_desc = $item['product_description'];
$mysql_mode = $item['delivery_mode'];
$mysql_cost = $item['course_cost'];
$insert->execute([customerID,mysql_desc,mysql_mode,mysql_cost]);
}

MySQL stops running queries after if statement

I've been stuck on this for a few hours now ...
Here's my code:
$SQLQuery1 = $db_info->prepare("SELECT COUNT(ID) FROM menusize WHERE typesize=:typesize");
$SQLQuery1->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery1->execute();
if($SQLQuery1->fetchColumn() > 0) {
$SQLQuery2 = $db_info->prepare("INSERT INTO menucatagorysize (menucatagory_ID,menusize_ID) VALUES (:catagoryid,(SELECT ID FROM menusize WHERE typesize=:typesize))");
$SQLQuery2->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery2->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery2->execute();
} else {
$SQLQuery2 = $db_info->prepare("INSERT INTO menusize (typesize) VALUES (:typesize);
SET #menusizeid=LAST_INSERT_ID();
INSERT INTO menucatagorysize (menusize_ID,menucatagory_ID) VALUES (#menusizeid,:catagoryid)");
$SQLQuery2->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery2->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery2->execute();
}
$SQLQuery3 = $db_info->prepare("SELECT DISTINCT(menuitem_ID) FROM menuprice WHERE menucatagory_ID=:catagoryid");
$SQLQuery3->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery3->execute();
$rows = $SQLQuery3->fetchAll(PDO::FETCH_ASSOC);
So, it will run through the if statement fine, running $SQLQuery1 and $SQLQuery2 (Which ever one is required) without any problems, errors or warnings. But, if it runs the else { part of the code, it will not run $SQLQuery3. Any thoughts?
Thanks :D
EDIT: Got it to work by doing $SQLQuery2=NULL in the else statement ... Sucks that I still cant figure out why it wouldnt work the original way.
It appears that you're trying to enforce a uniqueness constraint over the typesize column of your menusize table from within your application code. However, the database can do this for you—which will make your subsequent operations much simpler:
ALTER TABLE menusize ADD UNIQUE (typesize)
Now, one can simply attempt to insert the posted value into the table and the database will prevent duplicates arising. Furthermore, as documented under INSERT ... ON DUPLICATE KEY UPDATE Syntax:
If a table contains an AUTO_INCREMENT column and INSERT ... ON DUPLICATE KEY UPDATE inserts or updates a row, the LAST_INSERT_ID() function returns the AUTO_INCREMENT value. Exception: For updates, LAST_INSERT_ID() is not meaningful prior to MySQL 5.1.12. However, you can work around this by using LAST_INSERT_ID(expr). Suppose that id is the AUTO_INCREMENT column. To make LAST_INSERT_ID() meaningful for updates, insert rows as follows:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
Therefore, you can do:
$db_info->prepare('
INSERT INTO menusize (typesize) VALUES (:typesize)
ON DUPLICATE KEY UPDATE typesize=LAST_INSERT_ID(typesize)
')->execute(array(
':typesize' => $_POST['typesize']
));
$db_info->prepare('
INSERT INTO menucatagorysize
(menusize_ID, menucatagory_ID)
VALUES
(LAST_INSERT_ID(), :catagoryid)
')->execute(array(
':catagoryid' => $_POST['catagoryid']
));
$stmt = $db_info->prepare('
SELECT DISTINCT menuitem_ID
FROM menuprice
WHERE menucatagory_ID = :catagoryid
');
$stmt->execute(array(
':catagoryid' => $_POST['catagoryid']
));
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// etc.
}
(As an aside, the English word is spelled cat*e*gory, not cat*a*gory.)

MySQL DELETE failing randomly and very infrequently

I have a MySQL DELETE statement followed by an INSERT statement to ensure only one copy of a data set for a given ID. (There is a reason why I do not use UPDATE)
The problem is that I have noticed in a data set spanning 5000+ entries that there are 3 instances where the DELETE statement did not execute, but the INSERT statement did. The end result was duplicate entries for the given IDs.
Below is my code. On execution failure the php script will exit with an error message. It must be my level of experience. Any ideas what the problem could be?
//delete
$query_delete = "DELETE FROM q1_6_list WHERE q_id = '".mysql_real_escape_string($q[qID])."'";
$result = mysql_query($query_delete) or errorReport("Error in query: $query_delete. ".mysql_error());
//insert
$query_insert = "INSERT INTO q1_6_list (q_id, vehicle, quantity) VALUES";
$result = mysql_query($query_insert) or errorReport("Error in query: $query_insert. ".mysql_error());
if (!$result)
errorReport($result);
EDIT
On closer inspection I noticed that I did not add the below code right after $result = mysql_query($query_delete)....
if (!$result)
errorReport($result);
Could this also be required to catch all other error messages, or would mysql_query($query_delete) or errorReport... catch all error occurrences?
Your best bet here is to create a primary key on q1_6_list (q_id):
ALTER TABLE q1_6_list ADD PRIMARY KEY(q_id)
and use MySQLs REPLACE INTO syntax, so lose your delete and your insert becomes:
$query_insert = "REPLACE INTO q1_6_list (q_id, vehicle, quantity) VALUES";
You could simply run insert query only if delete query was successful:
if (mysql_query($query_delete)) {
mysql_query($query_insert);
}

Get ID of record when mysql returns duplicate error

Is there a way to retrieve the ID of a record (primary key) after an insert when the mysql error returns a duplicate key?
E.G. How I would go about it:
$sql = "INSERT INTO table (`col1`, `col2`) VALUES ('$val1', '$val2')";
$result = mysql_query($sql);
if($result){
$id = mysql_insert_id();
}
else {
if(stristr(mysql_error(), "duplicate"){
$sql = "SELECT `id` FROM `table` WHERE `col1`='$val1' AND `col2`='$val2'";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
$id = $row['id'];
}
else {
die(mysql_error());
}
}
Here I've had to do two sql statements which not only take time and effort, but duplicate code as well.
I cannot use ON DUPLICATE KEY UPDATE because I want to update a different table using the either the last inserted id, or the id of the record that cannot be duplicated.
So, am I right in what I'm doing? Or is there a way to get the id of the row?
Thanks
MySQL will not tell you which record holds the original value, you'll have to find out yourself. Here you are some tips:
Looking for the duplicate substring in the text of the error message does not look very robust. You can just test the value of mysql_errno() against the code for duplicate entry, which is 1062 (you can find all codes in the manual).
The mysql extension does not provide a mechanism to find out name of the violated key, so you'll have to use the non-robust approach of parsing the text of the error message:
if( preg_match("/Duplicate entry '.*' for key '(.*)'/Ui", mysql_error(), $matches) ){
$violated_key = $matches[1];
}else{
throw new Exception('Could not find violated key name');
}
Alternatively, just run a previous query (there's no reason to avoid it):
SELECT id
FROM table
WHERE col1=... AND col2=...
FOR UPDATE
The FOR UPDATE clause will lock matching rows to avoid race conditions (assuming InnoDB).

How do I get all the ids of the row created by one multiple row insert statement

I'm new to php. So, please forgive me if this seems like a dumb question.
Say i have a MySQL insert statement insert into table (a,b) values (1,2),(3,4),(5,6). table 'table' has a auto increment field called 'id'.
how can I retrieve all the ids created by the insert statement above?
It will be great if i get an example that uses mysqli.
You can't. I would suggest that you maintain your own ids (using guid or your own auto-increment table) and use it when you insert into the table.
But it's possible to get the auto-increment value for the last inserted using LAST_INSERT_ID():
http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
AngeDeLaMort's answer is almost right. Certainly, the most appropriate way to deal with the problem is to insert one row at a time and poll the insert_id or generate the sequence elsewhere (which has additional benefits in terms of scalability).
I'd advise strongly against trying to determine the last insert_id and comparing this the most recent insert_id after the insert - there's just too may ways this will fail.
But...an alternative approach would be:
....
"INSERT INTO destn (id, data, other, trans_ref)
SELECT id, data, other, connection_id() FROM source";
....
"SELECT id FROM destn WHERE trans_ref=connection_id()";
....
"UPDATE destn SET trans_ref=NULL where trans_ref=connection_id()";
The second query will return the ids generated (note that this assumes that you use the same connection for all 3 queries). The third query is necessary because connection ids to go back into the pool when you disconnect (i.e. are reused).
C.
In some cases, if you have another identifier of sort such as a UserID, you could filter your query by UniqueID's greater than or equal to mysql_insert_id(), limit by the number of affected rows and only display those by the user. This would really only work inside of a transaction.
$SQL = "INSERT INTO Table
(UserID, Data)
VALUES
(1,'Foo'),
(1,'Bar'),
(1,'FooBar')";
$Result = mysql_query($SQL);
$LastID = mysql_insert_id();
$RowsAffected = mysql_affected_rows();
$IDSQL = "SELECT RecordID
FROM Table
WHERE UserID = 1
AND RecordID >= '$LastID'
LIMIT '$RowsAffected'";
$IDResult = mysql_query($IDSQL);
as a follow up to AngeDeLaMort:
You could seperate your inserts and do it something like this:
$data = array (
array(1,2),
array(3,4),
array(5,6)
);
$ids = array();
foreach ($data as $item) {
$sql = 'insert into table (a,b) values ('.$item[0].','.$item[1].')';
mysql_query ($sql);
$id[] = mysql_insert_id();
}
Now all your new id's are in the $id array.
Maybe I can do this
$insert = "insert into table (a,b) values (1,2),(3,4),(5,6)";
$mysqli->query($insert);
$rows_to_be_inserted=3;
$inserted_id = $mysqli->insert_id // gives me the id of the first row in my list
$last_row_id = ($inserted_id+$rows_to_be_inserted)-1;
$mysql->query("select * from table where id between $inserted_id and $last_row_id");
what to you guys say?

Categories