Just trying to improve the efficiency of my code so a simply question:
I see quite often people declare their SQL query using one var ($sql) and then putting the result into another ($result). Is there any reason people do this apart from keeping things slightly tidier? I presume it's slightler better just to put the SQL query straight into mysql_query(). But there may be some other reason people are hiding.
It normally to make debugging easier as you go: if something is wrong with the SQL query for any reason, you can simply print the contents of the $sql variable.
Also, the contents of SQL queries can get pretty long and it looks rather unreadable to have it inside a function call past a certain length.
Well it leads to cleaner coding if there is an error.
If you have an error on line 151 and 151 is:
mysql_fetch_array(mysql_query("SELECT * FROM something")); //where is the error
That is much harder to read then:
Error on line 150 and lines 149 - 151 are:
$sql = "SELECT * FROM something";
$result = mysql_query($sql); // ahh the error is here
mysql_fetch_array($result);
There isn't anything magical about it. Putting your SQL into a variable has a lot of upsides and very few downsides; the same cannot be said for passing your SQL query straight to the mysql_query function.
For starters... you're using mysql_query directly? Most developers are going to have wrapped such functions into some kind of database object/controller, or they're going to use PDO or the like. In any event, putting the SQL into a variable allows you to easily swap out the thing you're passing the SQL to. When I update code to switch database access methodology, it makes it easier if I am changing a line like mysql_query($sql) rather than mysql_query('SELECT .... SUPER LONG QUERY ...').
When debugging, one can simply echo($sql). If one wants to do a count query separate from the data query:
$sql = ' FROM table_name WHERE `some_field` = 1';
$count = db::getField('SELECT COUNT(`id`) '.$sql);
$page_worth = db::getRows('SELECT `id`, `name` '.$sql.' LIMIT '.$page.', '.$per_page);
And so on, and so on. It really does boil down to preference, but I find this approach much more flexible and rapidly adaptable/debuggable.
Related
I want to get some data from a Sphinx server and pass it to MySQL to execute some queries. I'm new to PHP so probably I'm missing something here. I've looked for similar questions but can't find anything so maybe you can help me.
The error is in the first while. I'm pretty sure it's due to the $rown variable but don't know the reason. (I've verified that I can retrieve data from the connections so it is passing the data where the error lies - could be the sql syntax of the query but that seems fine).
Edited the code thanks to the comments below, now I get the error: Warning: mysqli_fetch_object() expects parameter 1 to be mysqli_result, boolean given in C:\Apache24\htdocs\test3.php on line 20. This is because the query failed, I still suspect it is because $rown.
$sphinxcon = mysqli_connect...
$mysqlcon = mysqli_connect...
$query = "SELECT names FROM iproducts LIMIT 0,1000";
$raw_results= mysqli_query($sphinxcon, $query);
//Until here works ok, now I want to pass $raw_results to MySQL
while ($row = mysqli_fetch_object($raw_results)) {
$rown = $row->names;
$mquery = "SELECT text FROM claims WHERE EXISTS ($rown) LIMIT 0,1000";
$mysqlresults = mysqli_query($mysqlcon, $mquery);
while ($final = mysqli_fetch_object($mysqlresults)) //this is line 20
{
printf ("%s<br />", $final->text);
}
}
Thanks :)
Well $row contains an object, so would have to use it as such, maybe
$rown = (string)$row->names;
... assuming you want the variable to contain the 'names' attribute you just SELECTed from Sphinx index.
As for the mysql EXISTS(), no idea what you really doing here, seems confused. How you structured it currently suggests that 'names' attribute in sphinx contains a complete SELECT query, that mysql could execute for the exists condition. That seems unlikely.
Guessing you meaning to more normal query something like
$mquery = "SELECT text FROM claims WHERE text LIKE '%$rown%' LIMIT 0,1000";
But that is subject to SQL injection, particully if names might contain single quotes. SO should escape it. Perhaps
$rown = mysqli_real_escape_string($mysqlcon, $row->names);
But might be worth reading up on prepared queries.
btw, the 'Error' you getting, is because you creating an invalid query and not dealing with it. So $mysqlresults is FALSE.
$mysqlresults = mysqli_query($mysqlcon, $mquery) or die("Mysql Error: ".mysqli_error($link)."\n");
I have checked everywhere thoroughly, and have gone through everything possible to find an answer to this. Besides saying "the code doesn't work" which obviously is not enough, I have yet to find anything that will even come close to this. I'm probably going to get downvotes, but let's see how this goes.
I am learning how to do prepared statements for a search query from the user end, and I have to do it for multiple queries. I have to bind parameters to these multiple queries, and then execute them and use them and receive multiple rows. This is most of my code, and what I currently have is not reporting any errors whatsoever. It just returns a blank white page.
I am doing this from a simple test.php file, and those are the results I'm getting.
Now for the code.
$prep1 = $test->prepare("SELECT * FROM sb__bans WHERE sb__bans.authid=? ORDER BY sb__bans.bid DESC");
$prep2 = $test->prepare("SELECT * FROM sb__bans AS bans INNER JOIN sb__admins AS admins ON bans.aid = admins.aid WHERE bans.authid=? ORDER BY bans.bid DESC");
$prep3 = $test->prepare("SELECT * FROM sb__bans AS bans INNER JOIN sb__servers AS servers ON bans.sid = servers.sid WHERE bans.authid=? ORDER BY bans.bid DESC");
$search = "steam";
$prep1->bind_param("s", $search);
$prep2->bind_param("s", $search);
$prep3->bind_param("s", $search);
$prep1->execute();
$prep2->execute();
$prep3->execute();
while($row = $prep1->fetch() && $admin = $prep2->fetch() && $sv = $prep3->fetch()) {
echo $row['test'];
echo $admin['test'];
echo $sv['test'];
}
The database is initialized above this as $test = new mysqli("localhost", "test", "test", "test");
$search = "steam" steam would be replaced with the the post variable of course, but for testing reasons I've removed that for now and am testing with just a simple variable.
What seems to be the problem here?
Thanks in advance.
Regarding the general question you asked.
There is not a single problem with having multiple queries prepared. While speaking of getting results from a prepared query, there is indeed a problem caused by the result buffering. In order to be able to execute another query, you have to call store_result()/get_result() right after execute.
Regarding the particular problem you have.
To get errors you have to ask PHP for them.
There is absolutely no point in making three queries, you have to make just one. If you have a trouble making one, ask another question marking it with mysql tag and bringing your 3 queries along.
Even for multiple queries it's just wrong idea to do multiple fetches in a single loop. Fetch your query results one by one.
Your mysqli syntax even for a single query is incomplete. You need to re-read your tutorial and practice on a single query first.
Two points:
Based on personal experience, you can only have one prepared statement in existence at a time. I suspect this is because the db requires each PS to have a session-unique name, and the PHP layer is passing some common default name rather than generating a unique name for each PS. By comparison, the PostgreSQL driver allows an optional name for each PS, but still allows only one unnamed PS to exist. Essentially this means that you must prepare, bind, execute and fetch one PS completely before you can prepare the next PS.
You're misusing mysqli_stmt::fetch(). fetch() returns only true or false, and is used to update variables which have previously been bound with mysqli_stmt::bind_result(). To retrieve values into a $row array, you must first call mysqli_stmt::get_result() to return a mysqli_result, and then call mysqli_result::fetch_array().
I always check/limit/cleanup the user variables I use in database queries
Like so:
$pageid = preg_replace('/[^a-z0-9_]+/i', '', $urlpagequery); // urlpagequery comes from a GET var
$sql = 'SELECT something FROM sometable WHERE pageid = "'.$pageid.'" LIMIT 1';
$stmt = $conn->query($sql);
if ($stmt && $stmt->num_rows > 0) {
$row = $stmt->fetch_assoc();
// do something with the database content
}
I don't see how using prepared statements or further escaping improves anything in that scenario? Injection seems impossible here, no?
I have tried messing with prepared statements.. and I kind of see the point, even though it takes much more time and thinking (sssiissisis etc.) to code even just half-simple queries.
But as I always cleanup the user input before DB interaction, it seems unnecessary
Can you enlighten me?
You will be better off using prepared statement consistently.
Regular expressions are only a partial solution, but not as convenient or as versatile. If your variables don't fit a pattern that can be filtered with a regular expression, then you can't use them.
All the "ssisiisisis" stuff is an artifact of Mysqli, which IMHO is needlessly confusing.
I use PDO instead:
$sql = 'SELECT something FROM sometable WHERE pageid = ? LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute(array($pageid));
See? No need for regexp filtering. No need for quoting or breaking up the string with . between the concatenated parts.
It's easy in PDO to pass an array of variables, then you don't have to do tedious variable-binding code.
PDO also supports named parameters, which can be handy if you have an associative array of values:
$params = array("pageid"=>123, "user"=>"Bill");
$sql = 'SELECT something FROM sometable WHERE pageid = :pageid AND user = :user LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute($params);
If you enable PDO exceptions, you don't need to test whether the query succeeds. You'll know if it fails because the exception is thrown (FWIW, you can enable exceptions in Mysqli too).
You don't need to test for num_rows(), just put the fetching in a while loop. If there are no rows to fetch, then the loop stops immediately. If there's just one row, then it loops one iteration.
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// do something with the database content
}
Prepared statements are easier and more flexible than filtering and string-concatenation, and in some cases they are faster than plain query() calls.
The question would be how you defined "improve" in this context. In this situation I would say that it makes no difference to the functionality of the code.
So what is the difference to you? You say that this is easier and faster for you to write. That might be the case but is only a matter of training. Once you're used to prepared statements, you will write them just as fast.
The difference to other programmers? The moment you share this code, it will be difficult for the other person to fully understand as prepared statements are kind of standard (or in a perfect world would be). So by using something else it makes it in fact harder to understand for others.
Talking more about this little piece of code makes no sense, as in fact it doesn't matter, it's only one very simple statement. But imagine you write a larger script, which will be easier to read and modify in the future?
$id = //validate int
$name = //validate string
$sometext = //validate string with special rules
$sql = 'SELECT .. FROM foo WHERE foo.id = '.$id.' AND name="'.$name.'" AND sometext LIKE "%'.$sometext.'%"';
You will always need to ask yourself: Did I properly validate all the variables I am using? Did I make a mistake?
Whereas when you use code like this
$sql = $db->prepare('SELECT .. FROM foo WHERE foo.id = :id AND name=":name" AND sometext LIKE "%:sometext%"');
$sql->bind(array(
':id' => $id,
':name' => $name,
':sometext' => $sometext,
));
No need to worry if you done everything right because PHP will take care of this for you.
Of course this isn't a complex query as well, but having multiple variables should demonstrate my point.
So my final answer is: If you are the perfect programmer who never forgets or makes mistakes and work alone, do as you like. But if you're not, I would suggest using standards as they exist for a reason. It is not that you cannot properly validate all variables, but that you should not need to.
Prepared statements can sometimes be faster. But from the way you ask the question I would assume that you are in no need of them.
So how much extra performance can you get by using prepared statements ? Results can vary. In certain cases I’ve seen 5x+ performance improvements when really large amounts of data needed to be retrieved from localhost – data conversion can really take most of the time in this case. It could also reduce performance in certain cases because if you execute query only once extra round trip to the server will be required, or because query cache does not work.
Brought to you faster by http://www.mysqlperformanceblog.com/
I don't see how using prepared statements or further escaping improves anything in that scenario?
You're right it doesn't.
P.S. I down voted your question because there seems little research made before you asked.
Okay, so I have some code here:
<?php
$rt = 'abc'; $imdb = 'defg';
if ($con = mysqli_connect($a,$b,$c,$d)) {
if (mysqli_query($con,"DELETE FROM blah WHERE a = '{$imdb}'")){
echo 'Deleted!';
if (mysqli_query($con, "INSERT INTO foo (c,d) VALUES ('{$rt}','{$imdb}')")){
echo 'Inserted after deletion!'
if (mysqli_query(...)) {
if (mysqli_query(....)) {
}
}
}
}
}
Some of my programs have many queries in a row, each of which is dependent on the result of a previous query. However, creating error handling for every single query and making sure that the logic of the code stays the same (not to mention staying readable) throughout can be a somewhat tedious and error prone process.
I would like to know a bit more about what is going on behind the scenes when a query is sent, because it would help me a lot with some of my questions. Namely, is it really necessary to write if (mysqli_query()) all the time for correct error handling, or is simply checking if the so-called 'master' connection exists enough?
That is, after I check mysqli_connect(), do I have to check every subsequent query within that connection to see if it went through (connection-wise, not logic-wise), or is it simply enough to check mysqli_connect() once, at the beginning of the program? It would sure make things a lot easier.
Also, while I'm looking at mysqli_multi_query() for more practical query management, I would prefer not to use it until I can fully understand the simpler query functions.
Thanks for your help!
Only few things for you to get it right
You have to distinguish an error from a query result. If your query depends on the result of the another one - it's all right to check the result. But if you want to check for the error - there are better ways.
In a properly written application a query error is a highly exceptional event, and there shouldn't be code written to handle it in place. It have to be done somewhere else.
mysqli can throw an exception in case of error, which is actually a Holy Grail you are looking for.
So, if you want to stop your code if one of queries failed, just set mysqli in exception mode and then pile your queries one after another.
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$con = mysqli_connect($a,$b,$c,$d);
$rt = 'abc'; $imdb = 'defg';
mysqli_query($con,"DELETE FROM ...");
mysqli_query($con, "INSERT INTO ...");
mysqli_query(...);
mysqli_query(...);
...
And two additional notes
if you want to undo a previous query if following one failed, then use transactions
you should NEVER write a query like you do, interpolating a variable directly into it. You ought to use prepared statements, substituting every variable with placeholder in the query.
new to php and am enrolled on a course, so can ask tutor tomorrow if this is more complicated than i think it might be!
I have an sql query, and it works fine. But I am trying to add and 'and' in the select statement.
This is what I have at the minute
$query = "SELECT * from table1 where table1.age <= " . $_POST['min_age'] ;
I have a 'region' input on my linked html page and want results to be returned only if the min_age and region values match those inputted by the user.
I have tried adding an 'and where' but it doesn't work and I am not sure if it is because of the multiple "'s or if what I am trying to do needs a different method?
Thanks
If you need multiple conditions, just separate them with AND:
... WHERE table1.age <= ? AND table1.region = ?
No need to use WHERE again. Just like you wouldn't need to use if() more than once if you were writing a complex condition in PHP.
PS: This isn't directly related to your question, but you should get into the habit of not putting $_POST or $_GET variables directly into your SQL queries. It's a good way to get hacked! Ask your tutor about "SQL injection," or read my presentation SQL Injection Myths and Fallacies.
I know you're just starting out, but if you were training to be an electrician, you would place a high priority on learning how to avoid being electrocuted or how to avoid causing a fire.
Here's how I would write your query using mysqli. One advantage of using query parameters is you never need to worry about where you start and end your quotes.
$query = "SELECT * from table1 where table1.age <= ? AND table1.region = ?";
$stmt = $mysqli->prepare($query) or trigger_error($mysqli->error, E_USER_ERROR);
$stmt->bind_param("is", $_POST["min_age"], $_POST["region"]);
$stmt->execute() or trigger_error($stmt->error, E_USER_ERROR);
The other good habit I'm showing here is to always report if prepare() or execute() return an error.
If you must interpolate variables into your SQL, first make sure you protect the variables either by coercing the value to an integer, or else by using a proper escaping function like mysqli_real_escape_string(). Don't put $_POST variables directly into the string. Also you don't have to stop and restart the quotes if you use PHP's syntax for embedding variables directly in double-quoted strings:
$age = (int) $_POST["min_age"];
$region = $mysqli->real_escape_string($_POST["region"]);
$query = "SELECT * from table1 where table1.age <= {$age}
AND table1.region = '{$region}'";