Update if row exists, otherwise insert new row into another table - php

I know this question but it didn't help.
I have a list with ids and values. Now I have to check if the id exists in my user database. If it does, then the row will be updated, otherwise I have to insert the id in another table (tmp_user).
Edit: Here is my try
IF NOT EXISTS (SELECT * FROM `wcf1_user` WHERE `steamID` = 1) THEN
INSERT INTO `wcf1_points_tmp` (`steamID`, `points`) VALUES (1, 2)
ELSE
// Update stuff......
END IF;
Result: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF NOT EXISTS (SELECT * FROM wcf1_user WHERE steamID = 1) THEN INSERT IN' at line 1
Thanks for your help. :)

If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row is performed. For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;

This is domain logic, which doesn't belong in the data tier. Do it in PHP instead:
<?php
// connect to the database
$DSN = "mysql:dbname=$dbname;charset=utf8";
$opt = array(PDO::MYSQL_ATTR_FOUND_ROWS => true);
$dbh = new PDO($DSN, $username, $password, $opt);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// one assumes you want to perform this operation atomically
$dbh->beginTransaction();
// attempt to update the row
$update = $dbh->prepare('
UPDATE wcf1_user
SET ......
WHERE steamID = ?
');
$update->execute(array($steamID));
// if the update didn't affect any rows
if (!$update->rowCount()) {
// insert into another table instead
$insert = $dbh->prepare('
INSERT INTO wcf1_points_tmp
(steamID, points)
VALUES
(?, ?)
');
$insert->execute(array($steamID, $points));
}
// tada!
$dbh->commit();
?>

I think you are missing semicolon at the end of insert statement, try following,
IF NOT EXISTS (SELECT * FROM `wcf1_user` WHERE `steamID` = 1) THEN
INSERT INTO `wcf1_points_tmp` (`steamID`, `points`) VALUES (1, 2);
ELSE
// Update stuff...... remember to add semicolon at the end
END IF;

Related

Syntax error in SELECT query inside PHP script

I am trying to create a query inside a PDO script that checks if a record exists if it does the query should update the record and if it doesn't exist it should create a new one.
The column that should only exist once in the table is not an INDEX key (cannot make it unique right now) so it is not set as unique and I cannot use the ON DUPLICATE KEY UPDATE
I would like to use this queries logic below to make it work:
$stmt = $conn->prepare('IF EXISTS (SELECT * FROM `Table1` WHERE `code`= :code )
UPDATE `Table1`
SET `code_stat` = 2
WHERE code = :code
ELSE
INSERT INTO `Table1` (`code`,`code_stat`)
VALUES (:code, 2 ) ' );
$stmt->execute([
'code' => $_POST['code']
]);
The problem is when executing the query I get the following error saying there is a syntax problem:
SQL syntax; check the manual that corresponds
to your MySQL server version for the right syntax to use near
'IF EXISTS (SELECT * FROM Table1 WHERE code= ? ) UPDATE Table1' at line 1
If you can't add a unique key to the table, you can attempt an update first, and if that doesn't update any rows, do an insert. Something like this:
$stmt = $conn->prepare('UPDATE `Table1` SET `code_stat` = 2 WHERE code = :code');
$stmt->execute(array(':code' => $_POST['code']));
if (!$stmt->rowCount()) {
// no rows updated, so insert
$stmt = $conn->prepare('INSERT INTO `Table1` (`code_stat`, `code`) VALUES (2, :code)');
$stmt->execute(array(':code' => $_POST['code']));
}
Note that you may need to set the PDO::MYSQL_ATTR_FOUND_ROWS attribute to ensure that the UPDATE query returns 1 if it finds the row but the value doesn't change. You must set that attribute when you make the connection e.g.
$conn = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
Why not write a stored procedure to handle this, similar to the below:
DROP PROCEDURE IF EXISTS db.SP_NEW_CODE;
CREATE PROCEDURE db.`SP_NEW_CODE`(IN `in_code` INT)
BEGIN
DECLARE numFound INT DEFAULT 0;
SET numFound=(SELECT * FROM `Table1` WHERE `code`= in_code);
IF (numFound=0) THEN
INSERT INTO `Table1` (`code`,`code_stat`) VALUES (in_code, 2 );
ELSE
UPDATE `Table1` SET `code_stat` = 2 WHERE code = in_code
END IF;
END;
From your code, simple execute CALL SP_NEWCODE(3); (for example, where 3 is the appropriate code value).

Last insert id issue--How to fetch it

I'm using $id = mysqli_insert_id($connection); to get the last inserted id, but in case if it updates any record in the table, it returns 0 as last inserted id.
Is there any way to handle this?
I want to get id each time weather it's inserting or updating.
Thanks
Edit
I need this id to be used for inserting data into table2
id from tab1
put data into tab2 where id from tab1 is FK
and most important, I'm not using the update with where clause
Here is my code that I'm using
$val = utf8_encode($val);
mysqli_set_charset($connection, 'utf8');
mysqli_query($connection, "SET NAMES 'utf8'");
mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 0;");
$sql = "INSERT INTO leaks($insert) VALUES($val)";
$sql .= " ON DUPLICATE KEY UPDATE `url` = '".mysqli_real_escape_string($connection,$data['url'])."';";
mysqli_query($connection, ($sql))or die(mysqli_error($connection)."<br />".print($sql));
$id = mysqli_insert_id($connection);
$proofs['leaks_id'] = $id;
mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 0;");
print_r($id);
$this->insertProofs($connection, $proofs);
connection::close_connection($connection);
Please note down that $this->insertProofs($connection, $proofs); inserts data to table2 on the base of key passed to it
On INSERT
After executing an INSERT query, using mysqli_insert_id() is absolutely fine.
On UPDATE
Depending on your update, you;
Would know the id's you're updating
Know the criteria to search for the id's from the update.
For example, if your UPDATE was something like;
UPDATE `foo` SET `x`='y' WHERE `a`='b'
You can then run
SELECT `id` FROM `foo` WHERE `a`='b'
to fetch the updated id's.
Edit
I see you're using ON DUPLICATE KEY UPDATE.
You can modify your query to become (assuming id is the primary auto_increment key)
ON DUPLICATE KEY UPDATE
`url` = '".mysqli_real_escape_string($connection,$data['url'])."',
id = LAST_INSERT_ID(id)
Then you can use mysqli_insert_id() regardless of if it was an UPDATE or INSERT
For example, if I run (with a record of id=2 exists; so we'll update);
INSERT INTO foobar (id, foo) VALUES (2, 'bar') ON DUPLICATE KEY UPDATE foo = 'baz', id = LAST_INSERT_ID(id);
SELECT LAST_INSERT_ID();
The output is 2, as that was the last insert id.

PHP check if MYSQL query was an insert or update

I have a simple MYSQL query:
INSERT INTO table (col1,col2) VALUES ('1','2')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2'
I use PHP PDO statements to query the database. Is there a way to know if the query executed resulted into a new inserted row or an existing was updated?
One way to do so is to get the number of rows before executing the query, then get the number of rows after executing the query, if they're not equal, it means a new row was inserted and if they are equal, it means a row was updated.
$sql = "SHOW TABLE STATUS LIKE 'TABLE_NAME'";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_before = $row['Rows'];
// Do your query here, afterwards
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_after = $row['Rows'];
// If condition
if($number_of_rows_before == $number_of_rows_after) // Update was executed
else // a new row was inserted.
Just use mysqli_affected_rows,it returns the number of rows affected by the last INSERT, UPDATE, REPLACE or DELETE query.
From PHP documentation:
In the case of "INSERT ... ON DUPLICATE KEY UPDATE" queries, the return value will be 1 if an insert was performed, or 2 for an update of an existing row.
see https://www.php.net/manual/en/function.mysql-affected-rows.php
From Mysql manual:
"With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if
the row is inserted as a new row and 2 if an existing row is updated."
See: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
This is the most reliable way to do it.
maybe you put the answer right into the query like:
INSERT INTO table (col1,col2, col_type) VALUES ('1','2','inserted')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2', col_type = 'updated'

Getting the final value to this MySQL query

I've got my database set up with three tables - code, tags, and code_tags for tagging posts.
This will be the SQL query processed when a post is submitted. Each tag is sliced up by PHP and individually inserted using these queries.
INSERT IGNORE INTO tags (tag) VALUES ('$tags[1]');
SELECT tags.id FROM tags WHERE tag = '$tags[1]' ORDER BY id DESC LIMIT 1;
INSERT INTO code_tags (code_id, tag_id) VALUES ($codeid, WHAT_GOES_HERE?)
The WHAT_GOES_HERE? value at the end is what I need to know. It needs to be the ID of the tag that the second query fetched. How can I put that ID into the third query?
I hope I explained that correctly. I'll rephrase if necessary.
Edit: Thanks for your help so far but I'm still struggling a bit in regards to what was pointed out - if it's already there I can't get the inserted ID...?
If you use INSERT IGNORE and a new record is ignored (because of a unique key violation) mysql_insert_id() and LAST_INSERT_ID() don't have a meaningful value.
But you can use INSERT ... ON DUPLICATE KEY UPDATE and LAST_INSERT_ID(expr) to set the data you expect LAST_INSERT_ID() to return in case of a doublet.
Step-by-step:
Let's assume you have a table tags like
CREATE TABLE tags (
id int auto_increment,
tag varchar(32),
dummy int NOT NULL DEFAULT 0, /* for demo purposes only */
primary key(id),
unique key(tag)
)
Inserting a tag twice results in a duplicate key violation because of unique key(tag). That's probably the reason why you've used INSERT IGNORE. In that case MySQL ignores the violation but the new record is ignored as well. The problem is that you want the id of the record having tag='xyz' regardless of whether it has been newly created or it was already in the database. But right now mysql_insert_id()/LAST_INSERT_ID() can oly provide the id of a new record, not an ignored one.
With INSERT ...ON DUPLICATE you can react on such duplicate key violations. If the new record can be inserted (no violation) it behaves like a "normal" INSERT. But in case of a duplicate key violation the part after ON DUPLICATE KEY is executed like an UPDATE statement for the record with that particular index value already existing in the table. E.g. (with an empty table tags)
INSERT INTO tags (tag) VALUES ('tag A') ON DUPLICATE KEY UPDATE dummy=dummy+1
This will simply insert the record as if there was no ON DUPLICATE ... clause. id gets the next auto-increment value, dummy the default value of 0 and tag='tag A'. Let's assume the newly create auto_increment value was 1. The resulting record stored in MySQL is (id=1, tag='tag A', dummy=0) and LAST_INSERT_ID() will return 1 right after this query. So far so good.
Now if you insert the same record again with the same query a violation occurs because of the first record (id=1, 'tag=tag A', dummy=0). For this already exisitng record the UPDATE statement after ON DUPLICATE KEY is executed, i.e. the record becomes (id=1, tag='tag A', dummy=1). But since no new record has been created there was also no new auto_increment value and LAST_INSERT_ID() becomes meaningless. So still the same problem as with INSERT IGNORE.
But there is a "special" construct that allows you to set the value LAST_INSERT_ID() is supposed to return after the ON DUPLICATE KEY UPDATE statement has been executed.
id=LAST_INSERT_ID(id)
Looks strange but it really only sets the value LAST_INSERT_ID() will return.
If you use the statement
INSERT INTO
tags
(tag)
VALUES
('xyz')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
LAST_INSERT_ID() will always return the id of the record having tag='xyz' no matter if it was added by the INSERT part or "updated" by the ON DUPLICATE KEY part.
I.e. if your next query is
INSERT INTO
code_tags
(code_id, tag_id)
VALUES
(4711, LAST_INSERT_ID())
the tags.id for the tag 'xyz' is used.
The self-contained example script uses PDO and prepared statements. It should do more or less what you want to achieve.
$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// set up temporary table and demo data
$pdo->exec('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))');
$pdo->exec('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)');
$pdo->exec("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')");
// prepare the statements
// set id=LAST_INSERT_ID(id), so LAST_INSERT_ID() gets a value even if the record is "ignored"
$stmtTags = $pdo->prepare('
INSERT INTO
tmpTags
(tag)
VALUES
(:tag)
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
');
$stmtTags->bindParam(':tag', $tag);
$stmtCodeTags = $pdo->prepare('INSERT INTO tmpCode_tags (code_id, tag_id) VALUES (:codeid, LAST_INSERT_ID())');
$stmtCodeTags->bindParam(':codeid', $codeid);
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// process (test)data
foreach($testdata as $data) {
// the parameter :codeid of $stmtCodeTags is bound to $codeid; assign it the "current" value
$codeid = $data['codeid'];
// split the tags
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// the parameter :tag is bound to $tag
// nothing more to do than to execute the statement
$stmtTags->execute();
// the parameter :codeid is bound to $codeid which was set to $codeid=$data['codeid']
// again nothing more to do than to execute the statement
$stmtCodeTags->execute();
}
}
unset($stmtTags);
unset($stmtCodeTags);
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
foreach( $pdo->query($query, PDO::FETCH_NUM) as $row ) {
echo join(', ', $row), "\n";
}
prints
1, tagA
1, tagC
2, tagC
2, tagD
2, tagE
edit2: In case the PDO-part of the script and the prepared statements are intimidating, here's the same thing using the old php-mysql module. But I urge you to use parametrized prepared statements. Doesn't have to be PDO but I happen to like it. E.g. the mysqli module provides prepared statements as well, the old mysql module doesn't.
$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());
// set up temporary table and demo data
mysql_query('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)', $mysql) or die(mysql_error());
mysql_query("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')", $mysql) or die(mysql_error());
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// "prepare" the statements.
// This is nothing like the server-side prepared statements mysqli and pdo offer.
// we have to insert the parameters into the query string, i.e. the parameters must
// be escaped so that they cannot mess up the statement.
// see mysql_real_escape_string() for string literals within the sql statement.
$qsTags = "
INSERT INTO
tmpTags
(tag)
VALUES
('%s')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
";
$qsCodeTags = "
INSERT INTO
tmpCode_tags
(code_id, tag_id)
VALUES
('%s', LAST_INSERT_ID())
";
foreach($testdata as $data) {
// in this example codeid is a simple number
// let's treat it as a string literal in the statement anyway
$codeid = mysql_real_escape_string($data['codeid'], $mysql);
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// now $tag is certainly a string parameter
$tag = mysql_real_escape_string($tag, $mysql);
$query = sprintf($qsTags, $tag);
mysql_query($query, $mysql) or die(mysql_error());
$query = sprintf($qsCodeTags, $codeid);
mysql_query($query, $mysql) or die(mysql_error());
}
}
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
$result = mysql_query($query, $mysql) or die(mysql_error());
while ( false!==($row=mysql_fetch_row($result)) ) {
echo join(', ', $row), "\n";
}
If I understand what you're attempting to achieve correctly, the second query is un-necessary - use mysql_insert_id to obtain the ID of previously inserted row, which is I presume what you need for "WHAT_GOES_HERE".

Check if exists, if so, update by 1++, if not insert

Hey guys quick question, I currently have an insert statement
$query= "INSERT into new_mail VALUES ('$to1', '0')"; where fields are username, and message_number
Currently what I would do to check if the entry exists, is do a select query then check the number of rows with mysql_num_rows (php). If rows==1 then I get the current message_number and set it equal to
$row['message_number']+1.
Then I update that entry with another query.
Is there an easier way to do all this in just mysql with just one query (check if exists, if not insert, if so update message_number, increase by 1)?
Depending on how your table is structured, you may be able to use the ON DUPLICATE KEY UPDATE (link to the MySQL manual) feature of INSERT:
INSERT into new_mail VALUES ('$to1', '0') ON DUPLICATE KEY UPDATE message_number=message_number+1
Use INSERT...ON DUPLICATE KEY UPDATE. The MySQL manual has an example which does almost exactly what you need:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
To make this work you need to add a UNIQUE index on the column that you use to check for duplicates. There is one important warning though:
In general, you should try to avoid using an ON DUPLICATE KEY clause on tables with multiple unique indexes.
Got a little confused by your question and your table structures but I think you want something like this.
INSERT INTO new_mail (username, message_number)
VALUES ($username, $message_number)
ON DUPLICATE KEY UPDATE message_number=message_number + 1;
This is presuming username is your primary key (more likely something like userid). Hope this helps.
EDIT: The ON DUPLICATE KEY UPDATE answers are better, but you could do this (eludes the select query):
Assuming you're using the mysqli extenson:
$db = //Some construction of mysqli object;
$sql = 'UPDATE tablename SET RowValue = RowValue + 1 WHERE message_number = ?';
$updateStatement = $db->prepare($sql);
$updateStatement->bind_param('i', $message_number);
$message_number = //Set message number;
$updateStatement->execute();
if ($updateStatement->affectedRows == 0) {
$sql = 'INSERT INTO tablename (RowValue, message_number) VALUES (?, ?)';
$insertStatement = $db->prepare($sql);
$insertStatement->bind_param('ii', $rowValue, $messageNumber);
$rowValue = something;
$messageNumber = something;
$insertStatement->execute();
}

Categories