I am using uploadify to upload pictures into server . Back end I am using PHP script . In PHP script I am inserting the file location and some other details in database in addition to copying to target location .
Problem :- If I am uploading 20 files , the PHP script is getting called 20 times and the database insert statement are getting called 20 times to insert for different images . Definitely calling database 20 times is inefficient way of doing it . Is there any way I can hold the file names( and location) and call the insert statement (in the end) only once to insert all the 20 records at once ?
Thanks for your help
Regards
Kiran
One approach I recommend for this is the use of a prepared statement through the mysqli extension. To quote on the main advantage:
The actual purpose to use a prepared
statement in sql is to cut the cost of
processing queries; NOT to separate
data from query. That's how it's being
used w/ php NOW, not how it was
designed to be used in the first
place. With SQL you cut the cost of
executing multiple similar queries
down by using a prepared statement..
Doing so cuts out the parsing,
validation and most often generates an
execution plan for said query up
front. Which is why they run faster in
a loop, than their IMMEDIATE Query
cousins do.
Source: http://us2.php.net/manual/en/mysqli.prepare.php#103730
To give an example on how you can utilize this:
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
if ($stmt = $mysqli->prepare("INSERT INTO `images` (`location`, `size`, `othervalues`) VALUES(?, ?, ?)")) {
foreach($images as $image) {
// s = string value
// i = integer value
$stmt->bind_param("sis", $image['location'], $image['size'], $image['othervalues']);
$stmt->execute();
}
$stmt->close();
}
// Your prepared statement failed, handle the error
else {
}
$mysqli->close();
I'd recommend reading up on mysqli::prepare and mysqli_stmt::bind_param for more information on how the process works. I also recommend reading up on prepared statements in the MySQL documentation.
Related
Here is an example.
$mysqli = new mysqli("localhost", "root", "123", "temp");
$mysqli->begin_transaction();
$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$test = $mysqli->multi_query($sql1);
$mysqli->commit();
There isn't any error in either of the queries, but when calling commit() the values are not stored in the database. The same works perfectly fine if split into separate queries and executed via query().
$mysqli->multi_query($sql1);
$mysqli->commit(); // This will result in a "Commands out of sync; you can't run this command now" error.
The above is identical to:
$mysqli->multi_query($sql1);
$mysqli->query("commit"); // This will result in a "Commands out of sync; you can't run this command now" error.
Whatever you put in $mysqli->query("...");, it WILL result in a "Commands out of sync" error, even with a simple SELECT 1;
The reason for this error is because ->commit() operation runs a single query (commit;). However, the results of the previous queries have not been read.
When a single query() operation is used, the MySQL server will answer with a response frame that depends on the query statement.
When using multi_query(), the following happens at MySQL communication protocol level:
A "Request Set Option" (as displayed in Wireshark) frame is sent with "multi statements" flag set to ON.
A frame containing the whole string transmitted to multi_query() as request.
MySQL server answers with a response that may contain different resultsets. The same is true when calling a stored procedure.
Solution 1
If you want to use multi_query(), you must have your start transaction / commit operations as part of it:
$mysqli = new mysqli("localhost", "root", "123", "temp");
$sql1 = "start transaction;"; // $mysqli->begin_transaction() is a convenience function for simply doing this.
$sql1 .= "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$sql1 .= "commit;"; // $mysqli->commit() is a convenience function for simply doing this.
$mysqli->multi_query($sql1);
/* As in "Solution 2", if you plan to perform other queries on DB resource
$mysqli after this, you must consume all the resultsets:
// This loop ensures that all resultsets are processed and consumed:
do {
$mysqli->use_result();
}
while ($mysqli->next_result());
*/
Solution 2
$mysqli = new mysqli("localhost", "root", "123", "temp");
$mysqli->begin_transaction();
$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$mysqli->multi_query($sql1);
// This loop ensures that all resultsets are processed and consumed:
do {
$mysqli->use_result();
}
while ($mysqli->next_result());
// Now that all resultsets are processed, a single query `commit;` can happen:
$mysqli->commit();
MySQL Reference: "Commands out of sync".
You shouldn't use multi query. Rewrite your code as follows
$mysqli->begin_transaction();
$mysqli->query("insert into test (Name) values ('pratik5')");
$mysqli->query("insert into test (Name) values ('pratik6')");
$mysqli->commit();
or, for the real-life inserts,
$mysqli->begin_transaction();
$stmt = $mysqli->prepare("insert into test (Name) values (?)");
$stmt->bind_param("s", $name);
$name = 'pratik5';
$stmt->execute();
$name = 'pratik6';
$stmt->execute();
$mysqli->commit();
First thing first, in the example you have shown you should not be using multi_query(). You have two separate statements which should be executed separately. See Your Common Sense's answer
multi_query() should be rarely used. It should be used only in situations when you already have a string composed of multiple queries, which you absolutely trust. Don't ever allow variable input into multi_query()!
Why commit() doesn't work after multi_query()?
Truth be told MySQL does throw an error on commit(), but mysqli is not able to throw the error as exception. Whether it is a bug or a technical limitation I do not know. You can see the error if you check manually $mysqli->error property. You should see an error as follows:
Commands out of sync; you can't run this command now
However, the exception is thrown correctly once you call use_result() or store_result().
$mysqli->multi_query(/* SQLs */);
$mysqli->commit();
$r = $mysqli->use_result(); // Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command now
The reason why MySQL says the commands are out of sync is because you can only execute a single command at a time per each MySQL session. However, multi_query() sends the whole SQL string to MySQL in a single command, and let's MySQL run the queries asynchronously. PHP will initially wait for the first result-set producing non-DML query (not INSERT, UPDATE or DELETE) to finish running before the control is passed back to PHP script. This allows MySQL to run the rest of the SQL on the server and buffer the results, while you can do some other operations in PHP and collect the results later. You can't run another SQL command on the same connection until you iterate over all the results from the previous asynchronous queries.
As pointed out in another answer, commit() will try to execute another command on the same connection. You are getting the out of sync error, because you simply haven't finished processing the multi_query() command.
MySQLi blocking loop
Each asynchronous query should be followed by a blocking loop in mysqli. A blocking loop will iterate over all the executed queries on the server and fetch the results one by one, which you can then process in PHP. Here is an example of such loop:
do {
$result = $mysqli->use_result();
if ($result) {
// process the results here
$result->free();
}
} while ($mysqli->next_result()); // Next result will block and wait for next query to finish
$mysqli->store_result(); // Needed to fetch the error as exception
You must have the blocking loop always, even when you know the queries are not going to produce any result-sets.
Solution
Stay away from multi_query()! Most of the time there's a better way of executing SQL files. If you have your queries separate, then don't concatenate them together, and execute each on their own.
If you really need to use multi_query(), and you would like to wrap it in transaction, you must put the commit() after the blocking loop. All the results need to be iterated over before you can execute the COMMIT; command.
$mysqli->begin_transaction();
$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$test = $mysqli->multi_query($sql1);
// $mysqli->commit();
do {
$result = $mysqli->use_result();
if ($result) {
// process the results here
$result->free();
}
} while ($mysqli->next_result());
$mysqli->store_result(); // To fetch the error as exception
$mysqli->commit();
Of course to see any of the mysqli errors you need to enable exception mode. Simply, put this line before new mysqli():
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
I have a PHP code that opens a CSV file using fgetcsv then reads through each rows then constructs a "Insert Into" query and attaches it to a variable named $MyQuery. This is the most understandable way for me when trying to update my database using information from a CSV file downloaded from the company website. This is working as I expected using the code below:
if (mysqli_multi_query($conn, $MyQuery))
{
do
{
/* store first result set */
if ($result = mysqli_store_result($conn))
{
mysqli_free_result($result);
}
} while (mysqli_next_result($conn));
}
Recently I learned about Prepared Statements and how they can secure your queries.
PROBLEM: How can I do multiquery with prepared statement in Procedural Mysqli way? I tried researching, a lot says it isn't possible. Some say it is possible but by creating different variables for each queries which is impossible for me as I will be inserting over 10000 records to my database from the CSV file. Is there any other ways to achieve this?
I'm thinking it can be done by looping through each records then doing a prepared-statement version of Insert Into but I thought doing 10000 Insert Into SQL commands would be very slow.
I am not 100% sure what you are asking but fallowing might work. First of all I would use pdo for connecting to a database. Fallowing is just a basic outline of what I think you want to do.
$pdo = new PDO('mysql:host=localhost;dbname=mydatabase', 'myusername', 'mypassowrd');
$query = "
INSERT INTO table (colum1, colum2, colum3)
VALUES (:info1, :info2, :info3)
";
$stmt = $pdo->prepare($query);
do
{
$stmt->execute(array(':info1'=>$my_info1, ':info2'=>$my_info2, ':info3'=>$my_info3));
} while( your condition);
There is two advantages for prepared statements. First is security and the second allows to do the same query over and over changing the values. This will make each of queries fast if you prepare them.
here is a ling that can explain more about prepared statements
http://php.net/manual/en/pdo.prepared-statements.php
I am adding a Procedural way of doing it using mysqli.
$query = "
INSERT INTO table (colum1, colum2, colum3)
VALUES (?, ?, ?)
";
if ($stmt = mysqli_prepare($conn, $query))
{
do
{
mysqli_stmt_bind_param($stmt, "sss", $my_info1, $my_info2, $my_info3);
mysqli_stmt_execute($stmt);
} while( your condition)
}
I am not sure why you are using mysqli_store_result(), mysqli_free_result(), or mysqli_next_result(). Since inserting rows produces no results.
UPDATED VERSION
<?php
$link = mysqli_connect("localhost", "root", "root", "metadata");
mysqli_set_charset($link, "utf8");
// Check connection
if($link === false){
die("ERROR: Could not connect. " . mysqli_connect_error());
}
// my form located in index.php posts the data here.
$add_movie_original_name = $_POST['movie_original_name'];
$add_movie_tr_name = $_POST['movie_tr_name'];
$add_movie_year = $_POST['movie_year'];
$sql = "INSERT INTO movie(movie_original_name,movie_tr_name,movie_year) VALUES('$add_movie_original_name','$add_movie_tr_name','$add_movie_year')";
if(mysqli_query($link, $sql)){
echo "Records added successfully.";
} else{
echo "ERROR: Could not able to execute $sql. " . mysqli_error($link);
}
// close connection
mysqli_close($link);
?>
I can't add records if there is an apostrophe in it. For instance, Uncle Sam's can't be added.
Here is the error I get. I tried to add a movie named Movie's Name.
ERROR: Could not able to execute INSERT INTO movie(movie_original_name,movie_tr_name,movie_year) VALUES('Movie's Name','','2014'). 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 's Name','','2014')' at line 1
(I deleted my comments, so line number will be different)
I think I should use a trick to escape the characters, but couldn't find out how.
You nee to be preparing your statements so that you aren't vulnerable to an SQL Injection attack. To do this, you should be using mysqli prepared statements. Your current code would look like this as a prepared statement
$mysqli = new Mysqli("localhost", "root", "root", "metadata");
$statement = $mysqli->prepare("INSERT INTO movie(movie_original_name,movie_tr_name,movie_year) VALUES('?','?','?')");
$statement->bind_param('sss', $add_movie_original_name, $add_movie_tr_name, add_movie_year);
$statement->execute();
Notice how in the actual SQL, I've replaced your variables with ?'s, this let's them be bound later on. In my bind_param method, the first parameter is how many variables you're binding, and what data types they are. There's one character for each variable, and they're all strings, so that character is "s". If you wanted to bind integers and strings, you would use
$statement->bind_param('sis', $string1, $int1, $string2);
Notice how the order of "sis" matches the order of what's passed it, string then integer then string again. According to the PHP Manual, there are four different types you can pass in, each with their own characters
s for string
i for integer
d for double
b for blob
So that's a short explanation of bound params. The problem you're having comes from the fact that your variables aren't escaped or bound, leaving them open to injection. This will fix your problem and make your code a little bit more secure.
Note: As pointed out by #bcintegrity, this isn't the be all end all for security. You're going to want to look into using htmlspecialchars() when echoing out your data that's been entered in by users in order to stop XSS (Cross Site Scripts) which can be very dangerous to not patch up.
Make it a priority to use prepared statements. Prepared statements simply send the query separate from the values, so the db knows the values are not to be run as code. Prepared statements escape the values automatically :)
Here is an example:
$sqli = #mysqli_connect("localhost", "root", "root","metadata");
if (!$sqli) {die("Can not connect to the database: " . mysqli_connect_error());}
$result = "INSERT INTO `movie`(movie_original_name,movie_tr_name,movie_year) VALUES (?,?,?)";
$stmt = mysqli_prepare($sqli, $result);
mysqli_stmt_bind_param($stmt,"sss",$_POST['movie_original_name'],$_POST['movie_tr_name'],$_POST['movie_year']);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
Be sure to use htmlspecialchars() if echoing values onto the page to protect from XSS:
$original_name_onscreen = htmlspecialchars($_POST['movie_original_name']);
$tr_name_onscreen = htmlspecialchars($_POST['movie_tr_name']);
$year_onscreen = htmlspecialchars($_POST['movie_year']);
Note: #Gareth Parker's example is object oriented style, similar to PDO, while mine is procedural style, similar to MySQL. Both are acceptable.
I'm trying to trouble shoot a problem with a prepared statement using mysqli in PHP. Is there a way to output the query that is actually sent to the server? For example if the WHERE clause to the query is determined from user input, is there a way to see the actual query generated containing the value from the WHERE clause after it's been sanitized? Let me know if the question isn't clear and I'll explain better.
So when mysqli_stmt_execute($finalQuery); callled, I want to see the query.
mysqli_info will return info on the last executed query.
http://php.net/manual/en/mysqli.info.php
$link = mysqli_connect("localhost", "my_user", "my_password", "world");
// Stuff to setup query...
$finalQuery = mysqli_prepare($link, $finalQuery);
mysqli_stmt_execute($finalQuery);
var_dump(mysqli_info($link));
I'm working on learning to use prepared statements with mysqli in PHP and usually, if I'm having a problem with a query I just echo it to the screen to see what it looks like as a first step.
How can I do that with a prepared statement?
I'd like to see the SQL statement after the variables are substituted.
Using prepared statements:
When you prepare the statement, it is sent to the MySQL server
When you bind the variables + execute the statement, only the variables are sent to the MySQL server
And the statement + bound variables are executed on the MySQL server -- without it re-doing the "preparation" each time the statement is executed (which is why prepared statements can be good for performance when the same statement is executed several times)
There is no "building" of an SQL query on the PHP side, so, there is no way to actually get that query.
Which means that if you want to see an SQL query, you have to use, well, SQL queries, and not prepared statements.
You can use PDOStatement->debugDumpParams to get some informations about the prepared statement (in case you're using pdo).
Prepared statements are logged in MySQL's general log:
For prepared statements that are executed with the mysql_stmt_prepare() and mysql_stmt_execute() C API functions, the server writes Prepare and Execute lines to the general query log so that you can tell when statements are prepared and executed.
[...] the server writes the following lines to the general query log:
Prepare [1] SELECT ?
Execute [1] SELECT 3
So for debugging purposes active the general log and keep an eye on that file.
edit: oh, the question has a [mysqli] tag... completely overlooked that.
If the statement isn't executed at all have you (double/tripple) checked that no error occurred along the way?
echo "<pre>Debug: start</pre>\n";
$mysqli = new mysqli('localhost', 'localonly', 'localonly', 'test');
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}
$result = $mysqli->query('CREATE TEMPORARY TABLE foo (id int auto_increment, x int, primary key(id))');
if ( false=== $result) {
die('error : '. $mysqli->error);
}
$stmt = $mysqli->prepare('INSERT INTO foo (x) VALUES (?)');
if ( false===$stmt ) {
die ('prepare() failed: ' . $mysqli->error);
}
$result = $stmt->bind_param('i', $x);
if ( false===$result ) {
die('bind_param() failed');
}
$x = 1;
$result = $stmt->execute();
if ( false===$result ) {
die('execute() failed: '.$stmt->error);
}
echo "<pre>Debug: end</pre>\n";
I usually do this when I need to debug a prepared sql with parameters.
Example of prepare and execute:
$sql = "SELECT VAL1, VAL2 FROM TABLE(?, '?', '?', '?', '?', ?, '?', '?', '?')";
$prep = ibase_prepare( $sql ) or die("Error");
$query = ibase_execute($prep, $param1, $param2, .....) or $err = true;
^^^^^^^^^^^^^^^^^^^^^^^
The easy way to debug the resulting SQL of the sentence it's:
printf( str_replace('?', '%s', $sql), $param1, $param2, ....);
^^^^^^^^^^^^^^^^^^^^^^
You only need to do one printf, replacing the ? on the prepared SQL string by one %s. printf will interpret all as one string, taking each parameter as placing it on each replaced %s.
I recently updated this project to include composer integration, unit testing and to better handle accepting arguments by reference (this requires updating to php 5.6).
I've created a set of classes that extend the default mysqli and mysqli_stmt classes to allow you to view a rendition of the potential query string, which should provide what you're looking for:
https://github.com/noahheck/E_mysqli
This is a (close to) drop-in replacement for you normal mysqli object that returns a custom mysqli_stmt object when you prepare() the query string. After binding your parameters, E_mysqli will allow you to view the resultant query string as a new property of the stmt object:
$mysqli = new E_mysqli($dbHost, $dbUser, $dbPass, $dbName);
$query = "INSERT INTO registration SET name = ?, email = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("ss", $_POST['name'], $_POST['email']);
$stmt->execute();
echo $stmt->fullQuery;
would result in:
INSERT INTO registration SET name = 'John Doe', email = 'john.doe#example.com'
There are some caveats with using this extension (explained in the README at the github project), but for troubleshooting troublesome areas of your application, or transitioning to an object oriented style from procedural, this should provide a level of help for most users.
As I've outlined in the github project, I don't have any practical experience using the mysqli extension, and this project was created at the request of users of it's sister project, so any feedback that can be provided from devs using this in production would be greatly appreciated.
Disclaimer - As I said, I made this extension.
Agreeing with Pascal MARTIN (+1) so I suggest another technique for debugging: var_dump() or log every variable you're inserting into the statement, that way you should be able to figure out if it is wrong data or logically wrong SQL.
You can use tool like Lottip. The idea is act like MySQL proxy. It parses MySQL packets, extracts query and it's params so you can see prepared statements with it's content.