SQL = IF NOT EXISTS in a prepared statement? - php

I'm trying to check if a record already exists in my table and if it doesn't I want to execute an insert... using a prepared statement. Can anyone tell me what's wrong below? I've written the code with error checking and it basically says the query is poo :) I am atrocious when it comes to SQL and pretty much anything programming related so I really do appreciate any wisdom that shared on this dodgy looking quest... Thanks!
$mysqli = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['db']);
$timestamp = time();
$stmt = $mysqli->prepare("IF NOT EXISTS (SELECT id FROM course_licence_cart WHERE userid = ? AND courseid = ? AND lmsid = ?) BEGIN INSERT INTO course_licence_cart (lmsid, userid, courseid, assigned_by, assigned_on) VALUES (?, ?, ?, ?, ?) END");
foreach($_POST['assignTo'] as $assignTo){
$stmt->bind_param('iiiiiiii', $assignTo, $_POST['course'], $core['id'], $core['id'], $assignTo, $_POST['course'], $userInfo['id'], $timestamp);
$stmt->execute();
}
FYI: This takes place after a form submission, I've checked ALL of the variables and they're all good, and the process itself works perfectly (I have it working without the IF NOT EXISTS), it's just this new query type mixed with prepared statements that has totally thrown me off.
-- UPDATE --
I ran the following directly:
IF NOT EXISTS (SELECT `id` FROM `course_licence_cart` WHERE `userid` = '175' AND `courseid` = '1' AND `lmsid` = '1') BEGIN INSERT INTO `course_licence_cart` (`lmsid`, `userid`, `courseid`, `assigned_by`) VALUES ('1', '175', '1', '175') END
In which I get the error:
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 `id` FROM `course_licence_cart` WHERE `userid` = '175' AND' at line 1

First of all the root of the error has nothing to do with prepared statements. It's just you can't use IF, BEGIN ... END blocks and other constructs out of the scope of a stored routine (procedure, function, trigger, event).
To prevent duplicates you can leverage INSERT IGNORE like so
$stmt = $mysqli->prepare("INSERT IGNORE INTO course_licence_cart (lmsid, userid, courseid, assigned_by, assigned_on) VALUES (?, ?, ?, ?, ?)");
foreach($_POST['assignTo'] as $assignTo){
$stmt->bind_param('iiiiiiii', $assignTo, $_POST['course'], $core['id'], $core['id'], $assignTo, $_POST['course'], $userInfo['id'], $timestamp);
$stmt->execute();
}
In order for this to work you have to have a unique constraint defined.
CREATE UNIQUE INDEX index_name
ON course_licence_cart (userid, courseid, lmsid);
Here is SQLFiddle demo
Now your code (after correcting syntax) could've worked in a stored procedure like so
DELIMITER //
CREATE PROCEDURE add_to_cart(IN _lmsid INT, IN _userid INT, _courseid INT, IN _assigned_by INT, IN _assigned_on DATETIME)
BEGIN
IF NOT EXISTS (SELECT *
FROM course_licence_cart
WHERE userid = _userid
AND courseid = _courseid
AND lmsid = _lmsid) THEN
INSERT INTO course_licence_cart (lmsid, userid, courseid, assigned_by, assigned_on)
VALUES (_lmsid, _userid, _courseid, _assigned_by, _assigned_on);
END IF;
END//
DELIMITER ;
Here is SQLFiddle demo
In this case php code would look like
$stmt = $mysqli->prepare("CALL add_to_cart (?, ?, ?, ?, ?)");
foreach($_POST['assignTo'] as $assignTo){
$stmt->bind_param('iiiiiiii', $assignTo, $_POST['course'], $core['id'], $core['id'], $assignTo, $_POST['course'], $userInfo['id'], $timestamp);
$stmt->execute();
}

Since you're coding in PHP, one thing to consider is doing a sql query to SELECT from that table, if nothing is returned, run an insert query.
I'm sure it can be doing through SQL, but I personally don't know how.
Good luck, sorry if this doesn't help.

in MYSQL there are 4 types of inserts available.
1)INSERT 2)INSERT IGNORE 3)INSERT ON DUPLICATE KEY 4)REPLACE
Please explore on them.
As far as I can understand your specific case can be handled by INSERT IGNORE command.
Note : I am assuming here that Mysqli is same as MYSQL
Update: now I know that mysqli is interface to MYSQL.But the concept of insert ignore will still be same.
example to understand use of INSERT IGNORE
consider below table.
CREATE TABLE person_tbl ( first_name CHAR(20) NOT NULL, last_name CHAR(20) NOT NULL, sex CHAR(10), PRIMARY KEY (last_name, first_name) );
here firstname&lastname forms primary key
now we run query
mysql> INSERT INTO person_tbl VALUES( 'Jay', 'Thomas');
it will add one row in table
if we will run the above sql it will fail with duplicate record exception.to prevent this exception we have 2 options
1)check if record already exists,if not then insert record.... we have to fire select and then insert query.
2) fire INSERT IGNORE SQL.. it will check the if record exist in table if exist then it will not insert record ,if not then inserts record.
for e.g
if we run query
mysql> INSERT IGNORE INTO person_tbl VALUES( 'Jay', 'Thomas');
this sql will not insert any record ..as record already exists
but
mysql> INSERT IGNORE INTO person_tbl VALUES( 'Vijay', 'Thomas');
it will insert one record in table.

Related

MySQLi prepared statement not taking valid query that calls a procedure

$prepared = $db->prepare("
SET #content = ?;
CALL add_interest_if_not_exists( #content );
SET #iid = (SELECT interests_id
FROM interests
WHERE content = #content);
REPLACE INTO profile_interests (user_id, interests_id, likes)
VALUES (
?,
#iid,
?
)
");
echo $db->error;
The error is:
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 'CALL add_interest_if_not_exists( #content ); SET #iid = (SELECT i' at line 3
The query works using MySQL workbench (hard-coded values). Why won't it in my prepared statement...
This is not a single query but a set of queries.
Therefore you have to run them one by one, each with separate query() or prepare()/execute() call.
Why not use a stored procedure?
CREATE PROCEDURE add_user_interest(p_content varchar(200), p_user_id int,p_likes int)
BEGIN
CALL add_interest_if_not_exists( p_content );
SET #iid = (SELECT interests_id
FROM interests
WHERE content = p_content);
REPLACE INTO profile_interests (user_id, interests_id, likes)
VALUES (
p_user_id,
#iid,
p_likes
);
END
End you will call as single prepared statement
$prepared = $db->prepare("CALL add_user_interest( ?, ? , ? )");

Concatenating a string and primary key Id while inserting

I have a user table in mysql, I insert data like this:
/* prepare query */
$query = 'INSERT INTO `users`(`first_name`,
`last_name`,
`gender`,
`username`,
`profile_picture`,
`provider`,
`provider_id`,
`provider_username`,
`provider_profile`,
`provider_profile_picture`,
`last_login`,
`created_date`,
`ip_address`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), INET_ATON(?))';
/* Prepare an insert statement */
$stmt = $mysqli->prepare($query);
if($stmt){
$stmt->bind_param("sssssssssss", $user['first_name'],
$user['last_name'],
$user['gender'],
$user['username'],
$user['profile_picture'],
$user['provider'],
$user['id'],
$user['username'],
$user['link'],
$user['profile_picture'],
$_SERVER['REMOTE_ADDR']);
$stmt->execute();
/* Execute the statement */
I would like to make the username be equal to 'user' + userId which is autoincremental primary key field.
so that the usernames get in order:
user1
user2
user3 and so forth
what is a slick way to accomplish that?
If user_id is an AUTO_INCREMENT primary key, then you can't do this with a single statement, even if you use a trigger.
The problem is that the AUTO_INCREMENT value isn't generated until after the BEFORE INSERT trigger runs, but you can't change username in the AFTER INSERT trigger.
So you just have to do the INSERT, then immediately do an UPDATE.
If user_id is not an AUTO_INCREMENT, but instead is something you specify yourself, then it's easy, you just do the concatenation in your PHP code before you pass the values as parameters.
Update: You can't do it with MySQL 5.7 generated columns either. It results in this error when you try to create the table:
Generated column 'username' cannot refer to auto-increment column.
Assuming the username is always 'user' + userid, the slickest way I can think of to do this is to have a table with everything except username in it, and a view on top of that table that adds username. You would then do any inserts and updates on the table, and any selects that require username could be done on the view.
CREATE VIEW userview AS
SELECT user_id, first_name, last_name, gender, profile_picture, provider,
provider_id, provider_username, provider_profile, provider_profile_picture,
last_login, created_date, ip_address, 'user' + user_id as username
FROM USER

PHP/MySQL Using Variables and INSERT INTO syntax

I am trying to pass variable values to a MySQL database table. I am using a PDO to get access to the database, and am able to echo the variable values that I want to insert to my browser. The only thing I can think of is that my syntax is wrong. I am clearly a novice at using PHP/MySQL.
I am not getting any errors. The info isn't going into my table. What am I doing wrong?
$sql = "INSERT INTO testquiz (version, points, passing_percent, gained_score, username, email, quiz_title, date)
VALUES ('$version', $points, $passing_percent, $gained_score, '$username', '$email', '$quiz_title', CURDATE() )";
Query to create table:
MySQL CREATE TABLE Query:
CREATE TABLE testquiz (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
version TEXT,
points INT,
passing_percent DOUBLE,
gained_score DOUBLE,
username TEXT,
email TEXT,
quiz_title TEXT,
date DATE NOT NULL
) DEFAULTCHARACTER SET utf8 ENGINE=InnoDB
When using PDO, the generally accepted practice is to use prepared statements for SQL, which essentially are a method used to sanitize your string input.
If your database connection object is $dbo then it would usually go like this.
Create a prepared statement by calling the prepare method on your database connection object:
$sql = $dbo->prepare("INSERT INTO testquiz (version, points, passing_percent, gained_score, username, email, quiz_title, date)
VALUES (:version, :points, :passing_percent, :gained_score, :username, :email, :quiz_title, CURDATE())");
As you can see, instead of passing in the variables I want for the values directly, I've created placeholders. Then, call the execute method on the $sql obect and pass the values in for the placeholders as key-value pairs in an array.
$sql->execute(array(":version" => $version, ":points" => $points, ":passing_percent" => $passing_percent, ":gained_score" => $gained_score, ":username" => $username, ":email" => $email, ":quiz_title" => $quiz_title));
This code passes in the values you define instead of the placeholders, and it properly escapes and sanitizes the variables you pass in for security, while executing your INSERT statement.
http://us1.php.net/pdo.prepared-statements
Change the insert statement to the below format and try.
$sql = "INSERT INTO testquiz (version, points, passing_percent, gained_score, username, email, quiz_title, date)
VALUES ('".$version."', '".$points."', '".$passing_percent."', '".$gained_score."', '".$username."', '".$email."', '".$quiz_title."', CURDATE())";

Mysql - conditional insert query with select and PDO

I am unable to understand on how to apply insert query with select statement:
I have gone through this question also:
MySQL INSERT from a SELECT with PDO
But where is the VALUES part??
Like I have this query to insert in Mysql and here I use Values also:
$db_conn->beginTransaction();
$query = $db_conn->prepare('INSERT INTO mytable (name, user_id) VALUES(:sname, :uid)');
foreach($UploadData AS $DataValue)
{
$query->execute(array(':sname' => $DataValue['Name'],':uid' =>$_SESSION['uid']));
}
$db_conn->commit();
My motto is to check if the name exists with the same uid it shouldn't import the data otherwise it should. But Where are the values part :/ I am blind :P
EDIT1: From MySQL INSERT from a SELECT with PDO
How will this code block work if no VALUES is supplied?
$sql_enc = '
INSERT INTO sessionid (enc_id, enc_pass, enc_date)
(SELECT AES_ENCRYPT(username, :aeskey), AES_ENCRYPT(pwd, :aeskey), DATE_ADD(NOW(), INTERVAL 15 SECOND) FROM users WHERE username = :username)
';
$res_enc = $pdo->prepare($sql_enc);
$res_enc->bindParam(':aeskey', $aeskey);
$res_enc->bindParam(':username', $username);
$res_enc->bindParam(':pwd', $username);
$res_enc->execute();
$res_enc = null;
There are two valid INSERT syntax:
INSERT
INTO `table` [(field1, field2)]
VALUES ( 'val1', 'val2' )
Or
INSERT
INTO `table` [(field1, field2)]
SELECT 'val1', 'val2'
the selected columns are your value fields.
#comments:
Replace:
http://dev.mysql.com/doc/refman/5.5/en/replace.html
Procedures:
http://dev.mysql.com/doc/refman/5.6/en/create-procedure.html
You are defining the parameters :sname and :uid in your loop. The method execute takes the params and "put them" inside your query before executing this one.
On other words, the query is compiled when you call prepare() and the parameters are applied when you call execute().
Edit:
Ok I didn't understand.
The query includes a "SELECT" part which gives the values to insert. With SELECT you must not write "VALUES", as the documentation says:
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE
col_name=expr
[, col_name=expr] ... ]

Populating multiple tables when creating new primary record

I have been working on my first webapp and I hit a bit of a wall. I have a table in my db set up as follows:
student_id(student_id, first_name, last_name, bdate, etc...)
I also have several tables for classes set up similarly to this
class_table(id, student_id, quiz_1, quiz_2, etc....)
student_id is how I would like to track everything, from my understanding, this would be a primary key that would become a foreign key on the class tables.
What I would like to do is create an entry for the student on each class table when the php script I am writing creates a new student. This is what my query looks like:
$query = "INSERT INTO student_id(0, '$first_name', '$last_name'.... etc);".
"INSERT INTO class_table(0, LAST_INSERT_ID(), '$quiz_1', $quiz_2'...etc)";
Is this the right way to do this? I keep getting an error from my mysqli_query... so I am guessing this is where the problem is. How would I achieve this?
mysqli_query() (and mysql_query()) will only execute a single query. You would need to perform two calls to mysqli_query() or use mysqli_multi_query(), which will execute multiple queries in one call.
You're missing the VALUES clause:
$query = "INSERT INTO student_id VALUES (0, '$first_name', '$last_name'.... etc);".
"INSERT INTO class_table VALUES (0, LAST_INSERT_ID(), '$quiz_1', '$quiz_2'...etc)";
and you will need to use the mysqli_multi_query() function. See the example at http://www.php.net/manual/en/mysqli.multi-query.php#106126:
if ($mysqli->multi_query($query)) {
$i = 0;
do {
$i++;
} while ($mysqli->next_result());
}
if ($mysqli->errno) {
echo "Batch execution prematurely ended on statement $i.\n";
var_dump($statements[$i], $mysqli->error);
}
You could also create a stored procedure, and call it with all the needed parameters:
CALL insert_student('$first_name', '$last_name', '$quiz_1', $quiz_2', ... etc);
Here's an example:
CREATE PROCEDURE add_student(
IN v_first_name VARCHAR(50),
IN v_last_name VARCHAR(50),
IN v_quiz_1 VARCHAR(255),
IN v_quiz_2 VARCHAR(255)
)
DETERMINISTIC
MODIFIES SQL DATA
proc: BEGIN
START TRANSACTION;
INSERT INTO student_id VALUES (0, v_first_name, v_last_name);
IF ROW_COUNT() <= 0 THEN
ROLLBACK;
SELECT 0 AS result;
LEAVE proc;
END IF;
INSERT INTO class_table VALUES (0, LAST_INSERT_ID(), v_quiz_1, v_quiz_2);
COMMIT;
SELECT 1 AS result;
END;

Categories