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 );
Related
I created a table
CREATE TABLE `pledge` (
`ID` INT(11) NOT NULL AUTO_INCREMENT,
`user` VARCHAR(45) NOT NULL,
`location` VARCHAR(45) NOT NULL,
`category` VARCHAR(45) NOT NULL,
`amount` DOUBLE NULL, NOT NULL,
`p_created` TIMESTAMP NOT NULL,
PRIMARY KEY (`ID`));
I added a unique key for user, location and category as index2.
I want to
INSERT a new record, if amount is not zero
UPDATE an existing record, if key (column user, location, category) exists
DELETE a record, if key (column user, location, category) exists AND amount is now zero
try {
$conec = new Connection();
$con = $conec->Open();
$p_created = date("Y-m-d H:i:s");
$sql = "INSERT INTO `pledge`(
`user`,
`location`,
`category`,
`amount`,
`p_created`)
VALUES (
:user,
:location,
:category,
:amount,
:p_created)";
$pre = $con->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
if ($pre->execute(array(
':user' => $user,
':location' => $location,
':category' => $category,
':amount' => $amount,
':p_created' => $p_created,
))) {
// echo "Successful";
}
} catch (PDOException $ex) {
echo $ex->getMessage();
}
} // try
I found that I can use something like "ON DUPLICATE KEY UPDATE col3='alpha';", but don't know how in above php code.
Since this exist, there might be also a possibility to delete instead of update if the amount is zero.
Update:
Thank you robbie for your hints. Somehow, I still make mistakes. I tried many ways, but ...
First I had to change the table. I dropped the primary key ID and added a new primary key containing the columns user, location, category.
To simplify my original question I updated only one column, but its actually two columns.
With that changes I was able to insert/update as I intended on the command line (Workbench):
INSERT INTO pledge(user, location, category, mypercent, mybalance, p_created)
VALUES ('5cdae45bdb5c5', 'Linz', 'A14', 50, 300, '2019-05-21 11:12:22')
ON DUPLICATE KEY UPDATE mypercent=20, mybalance = 53;
Works as expected. First time it will be inserted with mypercent=50 and mybalance=300 and the second time I try the exact same command, I get the record updated to mypercent=20 and mybalance=53
However, I cannot get THIS syntax into my PHP program. This is the latest try:
try {
$conec = new Connection();
$con = $conec->Open();
$p_created = date("Y-m-d H:i:s");
$sql = "INSERT INTO `pledge`(
`user`,
`location`,
`category`,
`mypercent`,
`mybalance`,
`p_created`)
VALUES (
:user,
:location,
:category,
:mypercent,
:mybalance,
:p_created)
ON DUPLICATE KEY UPDATE (`mypercent` = $percent[0], `mybalance` = $balance[0])
";
// worked on command line: UPDATE `pledge` SET `mypercent`='10',`mybalance`='10' WHERE `user`='5ce3770041037' and `location`='Linz' and`category`='A14';
// ON DUPLICATE KEY UPDATE `p_mypercent`=20, `mybalance` = 53;
$pre = $con->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
if ($pre->execute(array(
':user' => $user,
':location' => $locationid,
':category' => $category,
':mypercent' => $percent[0],
':mybalance' => $balance[0],
':p_created' => $p_created,
))) {
// echo "Successful";
} // if ($pre->execute(array(
} catch (PDOException $ex) {
echo $ex->getMessage();
} // try
This gives me a syntax error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '(mypercent = 10, mybalance = 10) mypercent, ' at line 17
There are several options, each with Pros and Cons
1) The simplest, as Nick said is comments is two write three queries. Don't do this - if you have multiple calls at the same time then another user may try to update the same record in between the read and write.
1b) If you insisted on doing this approach, you could do using transactions. https://dev.mysql.com/doc/refman/8.0/en/commit.html However there are risks with transactions that you need to handle, and it's a bit overkill for this operation.
2) The second simplest (and one I'd do) is to do an "ON DUPLICATE KEY" as you suggest in the question, and then a second operation to "delete if zero". This way, even if two events overlap, they will actually prevent each other from deleting the record, or both update the same record.
Critical note 1 : you need an UNIQUE index on user for this to work. Or drop "id" as a field, and use "user" as your primary key.
Critical note 2 : What you're missing is doing the maths in the SQL:
$sql = "INSERT INTO `pledge`(`user`, `location`, `category`, `amount`, `p_created`)
VALUES (:user, :location, :category, :amount, :p_created)
ON DUPLICATE KEY UPDATE `amount`=`amount`-VALUES(amount)";
which says insert the amount if it does not exist, otherwise, do the match and update the record.
Then a separate query
$sql = "DELETE FROM `pledge` WHERE `user`=:user AND `amount`=0";
(The "user=0" is not needed, but as you have user indexed, you don't have an index on "amount", it'll be faster. You could just clean up with $sql = "DELETE FROMpledgeWHEREamount=0";)
3) The fanciest way of doing this is to user a trigger inside the database. You can tell the SQL server that "if amount is zero, then delete". Or mix it all in a stored procedure, but they are both also overkill.
I want to use one form to insert into two different Microsoft sql tables. I tryed to use 2 inserts, but didnt work.
if (isset($_GET['submit'])) {
$sth = $connection->prepare("INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter','$Datum','$Verbleib','$DUTNr')");
echo "INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter',$Datum,'$Verbleib','$DUTNr')";
$sth->execute();
if($sth)
{
echo "";
}
else
{
echo sqlsrv_errors();
}
$MID = $connection->prepare("MAX(MID) as MID FROM DB.dbo.Fehler WHERE DB.dbo.Fehler.TestaufstellungID = '". $TestaufstellungID . "'");
$MID->execute();
$sth2 = $connection->prepare("INSERT INTO DB.dbo.Fehlerinfo (MID, Tester, Test, Ausfallbedingungen, Fehlerbeschreibung, Ersteller) VALUES ($MID, '$Tester','$Test','$Ausfallbedingungen','$Fehlerbeschreibung','$Ersteller')");
$sth2->execute();
To understand MID is the Primary key of table Fehler and ist the foreign key in the second table Fehlerinfo
Thats why i have the select work around to get the last MID and want to save it in a variable $MID to insert it into the second table.
Is there a smarter solution possible?
As I mentioned in the comments, generally the better way is to do the insert in one batch. This is very over simplified, however, should put you in the right direction. Normally you would likely be passing the values for the Foreign Table in a Table Value Parameter (due to the Many to One relationship) and would encapsulate the entire thing in a TRY...CATCH and possibly a stored procedure.
I can't write this in PHP, as my knowledge of it is rudimentary, but this should get you on the right path to understanding:
USE Sandbox;
--Couple of sample tables
CREATE TABLE dbo.PrimaryTable (SomeID int IDENTITY(1,1),
SomeString varchar(10),
CONSTRAINT PK_PTID PRIMARY KEY NONCLUSTERED (SomeID));
CREATE TABLE dbo.ForeignTable (AnotherID int IDENTITY(1,1),
ForeignID int,
AnotherString varchar(10),
CONSTRAINT PK_FTID PRIMARY KEY NONCLUSTERED(AnotherID),
CONSTRAINT FK_FTPT FOREIGN KEY (ForeignID)
REFERENCES dbo.PrimaryTable(SomeID));
GO
--single batch example
--Declare input parameters and give some values
--These would be the values coming from your application
DECLARE #SomeString varchar(10) = 'abc',
#AnotherString varchar(10) = 'def';
--Create a temp table or variable for the output of the ID
DECLARE #ID table (ID int);
--Insert the data and get the ID at the same time:
INSERT INTO dbo.PrimaryTable (SomeString)
OUTPUT inserted.SomeID
INTO #ID
SELECT #SomeString;
--#ID now has the inserted ID(s)
--Use it to insert into the other table
INSERT INTO dbo.ForeignTable (ForeignID,AnotherString)
SELECT ID,
#AnotherString
FROM #ID;
GO
--Check the data:
SELECT *
FROM dbo.PrimaryTable PT
JOIN dbo.ForeignTable FT ON PT.SomeID = FT.ForeignID;
GO
--Clean up
DROP TABLE dbo.ForeignTable;
DROP TABLE dbo.PrimaryTable;
As i mentioned the answer how it works for me fine atm.
if (isset($_GET['submit'])) {
$failInsert = ("INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter','$Datum','$Verbleib','$DUTNr')");
$failInsert .= ("INSERT INTO DB.dbo.Fehlerinfo (MID, Tester, Test, Ausfallbedingungen, Fehlerbeschreibung, Ersteller) VALUES (NULL, '$Tester','$Test','$Ausfallbedingungen','$Fehlerbeschreibung','$Ersteller')");
$failInsert .= ("UPDATE DB.dbo.Fehlerinfo SET DB.dbo.Fehlerinfo.MID = i.MID FROM (SELECT MAX(MID)as MID FROM DB.dbo.Fehler) i WHERE DB.dbo.Fehlerinfo.TestID = ( SELECT MAX(TestID) as TestID FROM DB.dbo.Fehlerinfo)");
$sth = $connection->prepare($failInsert);
$sth->execute();
}
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.
create table cmu_patient
( patient_id character varying(13) NOT NULL,
patient_hn character varying(7),
patient_fname character varying(50),
patient_lname character varying(50),
home_id integer,
CONSTRAINT cmu_patient_pkey PRIMARY KEY (patient_id),
CONSTRAINT Fk_home FOREIGN KEY(home_id)
REFERENCES cmu_home(home_id)
);
create table cmu_treatment
( treatment_id serial NOT NULL,
treatment_date date,
treatment_time time without time zone,
treatment_typecome character varying(100),
treatment_detail text,
patient_id character varying(13),
appointment_id character varying(5),
transfer_id character varying(5),
res_users_id integer,
CONSTRAINT cmu_treatment_pkey PRIMARY KEY (treatment_id),
CONSTRAINT Fk_patient FOREIGN KEY(patient_id)
REFERENCES cmu_patient(patient_id),
CONSTRAINT Fk_user_id FOREIGN KEY(res_users_id)
REFERENCES res_users(id)
);
$treatment_date = $GET_[...];
$treatment_time = $GET_[...];
$treatment_typecome = $GET_[...];
$treatment_note = $GET_[...];
$CID = $GET_[...];
this code -------- it's incorrect
INSERT INTO cmu_treatment(treatment_id, treatment_date, treatment_time,
treatment_typecome, treatment_detail, patient_id, appointment_id,transfer_id, res_users_id)
VALUES(NULL,'".$tratment_date."','".$treatment_time."','".
$treatment_typecome."','".$treatment_note."','".$CID."',NULL,NULL,NULL)
WHERE cmu_patient.patient_id = cmu_treatment.patient_id ;
i think that's wrong
i don't know if i want to write insert data into table with where cause i should write sql ?
thank :)
I suspect what you really want is an update, to change existing values in an existing record:
update cmu_treatment
set treatment_date = $treatment_date,
treatment_time = $treatment_time,
treatment_detail = $treatment_typecome,
treatment_note = $treatment_note
where patient_id = $CID;
(I'm leaving out the NULL values on the assumption that those shouldn't really change.)
If you do indeed want a new record, you can do:
INSERT INTO cmu_treatment(treatment_id, treatment_date, treatment_time,
treatment_typecome, treatment_detail, patient_id, appointment_id,
transfer_id, res_users_id
)
select NULL,'".$tratment_date."', '".$treatment_time."','".
$treatment_typecome."','".$treatment_note."','".$CID."', NULL, NULL, NULL;
You can write an INSERT statement populating target table with a SELECT statement. In the SELECT statement you can use WHERE condition.
So instead this query:
INSERT INTO table VALUES (....)
You must write:
INSERT INTO table
SELECT fields
FROM anothertable
WHERE condition
In your case, I think you must use an INSERT without WHERE condition if you want to insert only a row in your treatment table.
Tell me if you want to know further info
EDIT After comment
IMHO your statement must be:
INSERT INTO cmu_treatment
(treatment_id, treatment_date, treatment_time,
treatment_typecome, treatment_detail, patient_id, appointment_id,
transfer_id, res_users_id)
VALUES
(NULL,'".$tratment_date."','".$treatment_time."',
'".$treatment_typecome."','".$treatment_note."','".$CID."',NULL,NULL,NULL)
INSERT INTO `cmu_treatment`(`treatment_id`, `treatment_date`, `treatment_time`,
`treatment_typecome`, `treatment_detail`, `patient_id`, `appointment_id`,`transfer_id`, `res_users_id`)
VALUES(NULL,'".$tratment_date."','".$treatment_time."','".
$treatment_typecome."','".$treatment_note."','".$CID."',NULL,NULL,NULL)
WHERE `cmu_patient.patient_id` = `cmu_treatment.patient_id` ;
And you don't need (table name).(column).
Is this Inside "" ? If yes then you don't need '".$tratment_date."' you can use only '' so your code will look like this.
INSERT INTO cmu_treatment(treatment_id, treatment_date, treatment_time,
treatment_typecome, treatment_detail, patient_id, appointment_id,transfer_id, res_users_id)
VALUES(NULL,'$tratment_date','$treatment_time','
$treatment_typecome','$treatment_note','$CID',NULL,NULL,NULL)
WHERE `patient_id` = patient_id ;
And finally what is patient_id? Is it variable? If not IT MUST BE. Don't give same names to different things.
Actually I am think of a scenario like this:
Let's say we are managing a library which stores many books.
Our web application for the library allows user to add books to shelf.
The database has two tables, Books and Authors. Schemas are like these:
CREATE TABLE Books
(book_id int NOT NULL IDENTITY(1,1),
book_name nvarchar(100) NOT NULL,
author_id int NOT NULL,
PRIMARY KEY (book_id),
FOREIGN KEY ( author_id ) REFERENCES Authors(author_id),)
CREATE TABLE Authors
(author_id int NOT NULL IDENTITY(1,1),
author_name nvarchar(100) NOT NULL,
PRIMARY KEY (author_id))
Assume the request will get author name and book name to store the book on shelf.
It will automatically generate entry for author if there is no such author. But I want this operation to be a transaction.(I want everything rollback if something goes wrong.)
Can I get primary key before ending the transaction like this?
$server_name = "s3";
$connection_info = array( "Database"=>"bis_testing", "UID"=>"bisuser", "PWD"=>"111");
$conn = sqlsrv_connect( $server_name, $connection_info);
sqlsrv_begin_transaction( $conn )
$sql = "INSERT INTO Author(author_name)
values
(?);";
$author_name = 'Dr. Pro';
$stmt1 = sqlsrv_query( $conn, $sql, array($brand_name));
**// get author primary key to $author_pk**
$sql = "INSERT INTO Book(book_name, author_id)
values
(?,?);";
$book_name = 'Writing that works';
$stmt2 = sqlsrv_query( $conn, $sql, array($book_name, $author_pk));
if ($stmt1 && $stmt2) {
echo 'done';
}
If not, how should I do the job?
I don't see a way to get the last insert id like in MySQL, but you can get the last inserted id from SQL Server as discussed here: Best way to get identity of inserted row?
You could also look into a stored procedure that would do what you need to do, although I don't know how you would call it with these functions.