DELETE & INSERT or UPDATE best practice in SQL PDO - php

I'm trying to find an answer which is the best practice for following parts of code:
Let's say I have a single action for add and edit products
DELETE & INSERT
$product_id = 1;
$this->db->query("DELETE FROM `product_description` WHERE `product_id` = :product_id");
$this->db->bind(array(":product_id" => $product_id));
$this->db->execute();
$updateset = array(
":product_id" => $product_id,
":name" => $_POST["name"]
);
$this->db->query("INSERT INTO `product_description` SET `product_id` = :product_id, `name` = :name");
$this->db->bind($updateset);
$this->db->execute();
UPDATE or INSERT
$product_id = 1;
$updateset = array(
":name" => $_POST["name"]
":product_id" => $product_id
);
$this->db->query("UPDATE `product_description` SET `name` = :name WHERE `product_id` = :product_id");
$this->db->bind($params);
$this->db->execute();
if(!$this->db->row_count()) {
$this->db->query("INSERT INTO `product_description` SET `product_id` = :product_id, `name` = :name");
$this->db->bind($updateset);
$this->db->execute();
}
so which is better?

Neither option is good.
Problem 1:
They both are susceptible to a race condition. Another concurrent session might insert a row in the brief time between your two SQL statements.
Problem 2:
In the second solution, if the row-count of the UPDATE is zero, there may still be a matching row, because it won't count rows when the update doesn't change the value in the row.
Problem 3:
If you use the first option, and you use foreign keys, it won't work if there are any child rows referencing the row you delete. Either the delete will be blocked because of the child rows, or else you use ON DELETE CASCADE which means you will accidentally delete the child rows.
Better solution:
Define a UNIQUE constraint for product_id. Your usage suggests that this column would be a candidate key (i.e. there can only be one row per product_id).
Then use INSERT...ON DUPLICATE KEY UPDATE.
$updateset = array(
"product_id" => $product_id,
"name" => $_POST["name"]
);
$this->db->query("INSERT INTO `product_description`
SET `product_id` = :product_id, `name` = :name
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)");
$this->db->bind($updateset);
$this->db->execute();
Tip: Your array doesn't need to prefix the column names with :. That was required in early versions of PDO, but they changed it so this hasn't been a requirement for years.

The question is quite unclear but the second option looks much better.
The main reason is if your schema evolve and you end up with more columns than you already have, the first option will deleted existing data.
The second option on the other hand will make sure that any existing data will be kept in your database.

Related

If statement condition not running [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am checking the for the user_id (it is held in a session) - this is working. Then I am running a SELECT query for that user for the database table click_count. I am checking to see if that user has any records within it, ie: $page_count. If not, I want my INSERT statement to run to add that user to the database table along with other data.
The part I do not understand is it seems that my UPDATE query is always running. For example no matter which user I login with my query only updates the only user in the database table. IE: Bob is the only user in the click_count table, if I log in with Pete, Bob's record is being updated.
I have tested the value for $page_count and it equals 0, so my INSERT should be running. I have also tried if ($page_count === 0) {
Does anyone see anything I am missing?
$curPage = $_SERVER['PHP_SELF'];
$clicks = 0;
$setup = 0;
$page_total_count = 0;
var_dump($user_id);
$click_sql = "
SELECT *
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
$click_stmt = $con->prepare($click_sql);
$click_stmt->execute(array($user_id, $curPage));
$click_stmt_rows = $click_stmt->fetchAll(PDO::FETCH_ASSOC);
$page_count = $click_stmt->rowCount();
foreach ($click_stmt_rows as $click_stmt_row) {
$setup_status = $click_stmt_row['setup'];
$page_total_count = $click_stmt_row['page_count'];
}
if ($page_count == 0) {
$click_insert_sql = "
INSERT INTO click_count
(user_id, page_url, page_count, setup)
VALUES(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE page_count=page_count+1;
";
$click_insert_stmt = $con->prepare($click_insert_sql);
$click_insert_stmt->execute(array($user_id, $curPage, 1, $setup));
}
else {
$click_update_sql = "
UPDATE click_count
SET page_count=page_count+1
WHERE user_id = ?
AND page_url = ?
";
$click_update_stmt = $con->prepare($click_update_sql);
$click_update_stmt->execute(array($user_id, $curPage));
}
Table
click_count
CREATE TABLE `click_count` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`page_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`page_count` int(11) NOT NULL,
`setup` int(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
UNIQUE KEY `page_url` (`page_url`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Since there is only the one user in the table, there is no record "to insert/update", therefore
ON DUPLICATE KEY UPDATE failed you silently.
A regular UPDATE will suffice:
I.e. and as an example:
UPDATE table SET col_x = 0|1 WHERE col_y = ? // (boolean 0-1)
Note:
If ever you wish to increase a column by counting later on, the syntax would be:
UPDATE table SET col_x = col_x + 1 WHERE col_y = ?
In regards to your asking about how you could improve on your code:
#Fred-ii- Thanks. Yes, it is working now how I want, but if there are ways to improve the code I am always willing to try to learn it. I just remembered people in the past saying that I didn't need the update query at all with the duplicate key update. – Paul
You could use named placeholders :name rather than ? since they are easier to keep track of, but this is of course a matter of opinion that I feel is also shared by many and not just myself.
Footnotes/credits:
I would like to also give credit to the following comment:
"If you always fall into update indicates that $page_count is not zero.. Try to echo() it to see maybe.. I would probably first try to add another user into click_count table and then it may become easier to see where it goes wrong.. – johnyTee"
where the OP responded with:
"#Fred-ii- I figured it out. I used johnyTee's advise and tried adding another user to the database manually and it wouldn't let me because of the unique index for the page_url column. I then removed the unique index from it and now it works perfectly. Thanks for the help! – Paul"
from PHP PDO doc http://php.net/manual/en/pdostatement.rowcount.php
PDOStatement::rowCount() returns the number of rows affected by a
DELETE, INSERT, or UPDATE statement.
if you need th number of rows in select you should use somethings like
$sql = "SELECT *
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
$result = $con->prepare($sql);
$result->execute();
$number_of_rows = $result->fetchColumn();
It may be '0' (a string). You can use intval to convert it to an integer.
$page_count = intval( $click_stmt->rowCount() );
http://php.net/manual/en/function.intval.php
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.
try like this:
$sql = "SELECT count(*)
FROM click_count
WHERE user_id = ?
AND page_url = ?
";
if ($res = $conn->query($sql)) {
/* Check the number of rows that match the SELECT statement */
if ($res->fetchColumn() > 0) {
//insert
}else {
//update
}
}

INSERT... ON DUPLICATE KEY or UPDATE... SET

I have a INSERT.. ON DUPLICATE KEY which just will not update, instead inserts a new row. I have a 2 unique keys but still it is just inserting new rows.
I have tried unique keys on multiple rows to see if that was the problem, but still getting the same issue.
So i was wondering would using UPDATE table SET ... be more suitable or should i stick with INSERT.. ON DUPLICATE.
I have a products table, which i want my administrators to be able to change/update whenever they like. At the moment i have a INSERT .. ON DUPLICATE but it is giving me endless problems.
I have a unique key on both the primary key(i know it is not needed, for test purposes only) and the restaurant name, as logically i thought if having a unique key on the restaurant name alone is not working. Maybe by making both the restaurant id and restaurant name a unique key the function would update row and not insert a new one. But still not working, this is my last resort as i cannot figure out the issue.
UPDATE
A new row is only inserted if i change the restaurant name, if i change anything else the table updates.
if (isset($_POST['Save-dets'])) {
$r_id = mysqli_real_escape_string($dbc, $_POST['Resturant_ID']);
$r_name = mysqli_real_escape_string($dbc, $_POST['rest_name']);
$r_desc = mysqli_real_escape_string($dbc, $_POST['Desc']);
$insert_rest = "INSERT INTO Rest_Details
( Resturant_ID
, Resturant_name
, Resturant_des)
VALUES
( ?
, ?
, ?
)
ON DUPLICATE KEY
UPDATE Resturant_ID = ?
, Resturant_name = ?
, Resturant_des = ?";
$run_query = $dbc->prepare($insert_rest);
$run_query->bind_param($c, 'ssssss', $r_id, $r_name, $r_desc,$r_id, $r_name, $r_desc );
if (!$run_query->execute($run_query))
if ($execute) {
echo "<script> alert('Addrrss Saved')</script>";
} else {
echo mysqli_error($dbc);
}
}
CREATE TABLE `Rest_Details` (
`Resturant_ID` bigint(255) NOT NULL AUTO_INCREMENT,
`Resturant_name` varchar(100) NOT NULL,
`Resturant_des` varchar(200) NOT NULL,
PRIMARY KEY (`Resturant_ID`),
UNIQUE KEY `Resturant_ID` (`Resturant_ID`,`Resturant_name`),
) ENGINE=InnoDB AUTO_INCREMENT=243 DEFAULT CHARSET=utf8
Your opinion on this matter would be very helpful.
Please check your usage at
$run_query->bind_param($c, 'ssssss', $r_id, $r_name, $r_desc,$r_id, $r_name, $r_desc );
You are mixing object-oriented style parameters with functional style parameters - see http://php.net/manual/en/mysqli-stmt.bind-param.php
Based on the at documentation it would either be
$run_query->bind_param('ssssss', $r_id, $r_name, $r_desc,$r_id, $r_name, $r_desc );
or
mysqli_stmt_bind_param($c, 'ssssss', $r_id, $r_name, $r_desc,$r_id, $r_name, $r_desc );

Ordering Mechanism in Mysql

I have a matter in PHP & Mysql Project.
Simply, I have two tables project and project features,
Every project has as specific features.
CREATE TABLE projects (
ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(255) NOT NULL
);
CREATE TABLE projects_features (
projectId INT NOT NULL,
name varchar(255) NOT NULL,
value varchar(255) NOT NULL,
weight INT NOT NULL
);
INSERT INTO projects VALUES (NULL,'project1');
INSERT INTO projects VALUES (NULL,'project2');
INSERT INTO projects_features VALUES (1,'Feature1','Feature1 Value',1);
INSERT INTO projects_features VALUES (2,'Feature2','Feature2 Value',2);
INSERT INTO projects_features VALUES (1,'Feature3','Feature3 Value',3);
INSERT INTO projects_features VALUES (2,'Feature4','Feature4 Value',4);
INSERT INTO projects_features VALUES (1,'Feature5','Feature5 Value',5);
I Get the Project features by:
SELECT * FROM projects_features WHERE projectId = 1 ORDER BY weight ASC;
So the bigger weight will be down and lower weight will be Up.
Now,
In My View I have move up and move down buttons, so I can re-sort project features.
I can firstly select the current item weight then select the upper item weight ,
then type two update queries to exchange the weight between the two rows,
but it's not a professional way , I don't like to use four queries.
I need to do it in one query Instead of four queries.
Can anybody help please ?
Here's how I'd tackle this, assuming I've understood the question.
First, I'd add a featureId column to projects_features, and make (projectId, featureId) the composite primary key. This isn't actually necessary to my solution; it just makes the rest a whole lot easier. For the next part you need to be able to reference individual records in projects_features.
Next, I'd have the Move buttons populate an array in PHP, like
$update_list = array();
$update_list[i] = array(':project_id' => $proj_id,
':feature_id' => $feat_id,
':new_weight' => $weight);
Finally, I'd do the update through a PHP function that encapsulates the UPDATE statements, like this:
function updateWeights($update_array, $dbh)
{
$sql = 'UPDATE project_features
SET weight = :new_weight
WHERE projectId = :project_id
AND featureId = :feature_id'
$stmt = $dbh->prepare($sql);
foreach ($update_array as $update_item)
{
$stmt->execute($update_item);
}
}
Note I'm using PDO here; it could also be done with mysqli, although mysqli doesn't support named bind parameters so the syntax would be slightly different, something like
function updateWeights($update_array, $dbh)
{
$sql = 'UPDATE project_features
SET weight = ?
WHERE projectId = ?
AND featureId = ?'
$stmt = $dbh->prepare($sql);
foreach ($update_array as $update_item)
{
$stmt->bind_param('i', $update_item[':new_weight']);
$stmt->bind_param('i', $update_item[':project_id']);
$stmt->bind_param('i', $update_item[':feature_id']);
$stmt->execute();
}
}
Also note that I haven't actually run this code, and so make no representation that it is free of syntax or other errors.
I hope that helps.

INSERT ...ON DUPLICATE KEY UPDATE when key is not always known?

I'm trying to update a record if the key is known else I want to insert it and get the inserted id, currently I have:
if(isset($data['applicationId']))
{
//update
$sql="
UPDATE myTable SET data='jsonstring' WHERE id = {$data['applicationId']}
";
}
else
{
//insert and get id
$sql="
INSERT INTO myTable SET data='jsonstring'
";
}
Is it possible to simplify the above to one query using INSERT ...ON DUPLICATE KEY UPDATE even when the key is not always known ?
I've tried this:
INSERT INTO myTable
(
id,
data
)
VALUES
(
?, # <- I may not know this!!
'jsonstring'
)
ON DUPLICATE KEY UPDATE
data = 'jsonstring'
Thanks for any suggestions.
Yes, you can do that, assumed id is your primary key and auto_increment. You will have two different queries, one if you know the applicationId and one when you not knowing it.
The first, when you know it:
INSERT INTO myTable
(
id,
data
)
VALUES
(
1337, # <- insert id
'jsonstring'
)
ON DUPLICATE KEY UPDATE
data = 'jsonstring';
And the one if the applicationId is unknown:
INSERT INTO myTable
(
id,
data
)
VALUES
(
NULL, # <- This will cause mysql to use a auto_increment value
'jsonstring'
)
ON DUPLICATE KEY UPDATE
data = 'jsonstring';
So you can conclude this to:
$sql="INSERT INTO myTable
(
id,
data
)
VALUES
(" .
isset($data['applicationId']) ? $data['applicationId'] : 'NULL'
.",
'jsonstring'
)
ON DUPLICATE KEY UPDATE
data = 'jsonstring';
";
But be aware of How can I prevent SQL-injection in PHP?
Happy coding
Please forgive because your question is not 100% clear. However, the concept I can tell is that you want to be able to ask more than 1 query on 1 sql statement. That can be done with a multi-query command. However, if you want some of your data from a query placed in your next query I do not think it will work. Link provided for multi_query
http://php.net/manual/en/mysqli.quickstart.multiple-statement.php
First, Simple update query will run. If it runs successfully, it will not go to if condition and your ID will be the one which was used in updating.
And, if that ID is not available (means update query fails, $Query will be false), so pointer jumps to if condition and insert the query. Now, new Inserted ID we can get.
$ID=$data['applicationId'];
$Query=mysql_query("UPDATE myTable SET data='jsonstring' WHERE id='$ID' ");
if(!$Query)
{
$InsertQuery=mysql_query("INSERT INTO myTable SET data='jsonstring'");
$ID=mysql_insert_id();
}
So, $ID will be your ID.(either updated or currently inserted)

PHP Insert or update table

Let me explain what I need and canot get :(
I have to DB one i main the other is just getting part of data from the firs one.
This is my code:
foreach($id_product_array AS $id_product) {
$resultf = mysql_query("SELECT * FROM db1_available_product WHERE id_product='".$id_product."'");
while($rowi = mysql_fetch_array($resultf)) {
$aa1=$rowi['id_product'];
$aa2=$rowi['date'];
$aa3=$rowi['available'];
$aa4=$rowi['published'];
mysql_query("INSERT INTO aa_bb.db2_available_product (`id_product`, `date`, `available`, `published`) VALUES ('".$aa1."','".$aa2."', '".$aa3."', '".$aa4."') ON DUPLICATE KEY UPDATE `id_product` = '".$aa1."', `date` = '".$aa2."', `available` = '".$aa3."', `published` = '".$aa4."'");
}
The problem is that this multiples the record in DB2 so I am now in millions!!!
Its set up as cron job on 1h basis.
What I need is ether it checks what is existing and don't touch it or if need on update or insert.
The other solution would be to delete the whole table in DB2 then to insert a fresh one from DB1
You can simplify your query like so:
INSERT INTO tbl2 (column1, column2)
SELECT column1, column2 FROM tbl1
ON DUPLICATE ...
See the documentation
You are looking for MySQL's proprietary REPLACE command. It has the same syntax as a regular INSERT, but it checks for duplicate primary key before inserting, and if it is found it will do an UPDATE instead:
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.
Of course you will have to define a unique PK/index on your table that allows this functionality to work.
here is an update!
I solved the problem :)
Thanks s to Niels because he made me rethink my strategy so the solution was simple.
In the DB1 and DB2 there is and ID filed
A added
$aa5=$rowi['id'];
so that made ON DUPLICATE KEY UPDATE work correctly!
foreach($id_product_array AS $id_product) {
$resultf = mysql_query("SELECT * FROM db1_available_product WHERE id_product='".$id_product."'");
while($rowi = mysql_fetch_array($resultf)) {
$aa5=$rowi['id'];
$aa1=$rowi['id_product'];
$aa2=$rowi['date'];
$aa3=$rowi['available'];
$aa4=$rowi['published'];
mysql_query("INSERT INTO aa_bb.db2_available_product (`id`,`id_product`, `date`, `available`, `published`) VALUES ('".$aa5."','".$aa1."','".$aa2."', '".$aa3."', '".$aa4."') ON DUPLICATE KEY UPDATE `id` = '".$aa5."',`id_product` = '".$aa1."', `date` = '".$aa2."', `available` = '".$aa3."', `published` = '".$aa4."'");
}
and it seams that it is working OK!
:)

Categories