I want to be able to call a query function and based on the results they are put into an object that I can call outside the function. This is what I have:
<?php
function query($sql) {
global $r;
$database = "theatre";
$db_conn = pg_connect ("host=localhost port=5432
dbname=$database user=postgres");
if (!$db_conn): ?>
<h1>Failed connecting to postgres database <?php echo $database;?></h1>
<?php
exit;
endif;
$ep = pg_query ($db_conn, $sql);
while ($r = pg_fetch_object ($ep)) {
}
pg_free_result($ep);
pg_close($db_conn);
}
query('select * from test');
echo $r->fname;
I don't want to hardcode any column names into the function. So fname is an example but it will be dynamic based on the query. I have a while loop but not sure what to put in it.
Value returned by function pg_fetch_object is already an object, so no need to loop through results. Just return it:
return pg_fetch_object ($ep);
Then just use the returned results without referring to this awkward global variable $r.
$r = query('select * from test');
echo $r->fname;
Also, no need to free the resource with pg_free_result. It gets freed automatically at end of scope it exists within (that is current function in this case). Calling pg_close may also slow down your script, as the connection will have to be recreated anew next time you call the function. I'd suggest storing connection in local static variable and not closing it at all - it will be closed automatically.
function query($sql) {
$database = "theatre";
static $db_conn;
if (!$db_conn) {
$db_conn = pg_connect ("host=localhost port=5432 dbname=$database user=postgres");
}
...
As a long term solution I'd recommend researching subject of Object-Relational Mapping and learning one of available ORM-ish tools (i.e. Zend Db Table, Doctrine, Propel, or one of existing implementations of ActiveRecord pattern).
Related
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}
I have come to the conclusion that using mysqli in an OO approach is better than a procedural approach. (Source: Why is object oriented PHP with mysqli better than the procedural approach?). But I'm not quite sure if what I am doing is really all that more efficient than what I was doing before.
I have a function that runs sql queries. This is what my block of code looked like:
Database connection:
function connectDB(){
$con = mysqli_connect(server, username, password, database);
return $con;
}
Query function:
function executeQuery($payload){
$con = connectDB;
$result = mysqli_query($con, $payload);
return $result;
}
As you can see, that's not very efficient because I'm creating a new database connection every time executeQuery is called. So I figured I'd try it using OOP.
Database connection (OOP):
function connectDB(){
$con = new mysqli(server, username, password, database);
return $con;
}
Database query (OOP):
function executeQuery($payload){
$con = connectDB();
$result = $con->query($payload);
return $result;
}
Now to me, it seems that I am obviously doing something wrong. Each time a query is called I am re-instantiating the mysqli class and I assume that mean's that I am making another database connection.
So how do I do this properly and efficiently?
So how do I do this properly and efficiently?
This really has nothing to do with using MySQLi in a procedural versus OOP way.
What this has to do with is the following line:
$con = connectDB();
This will recreate the database connection on every query. Which, as you noted, is not efficient.
There are many ways to solve this. For example:
Use the mysqli class directly.
Pass $con to executeQuery() (Dependency Injection)
Create a DB class with both connectDB() and executeQuery().
I usually use mysqli directly as I see no reason to wrap the native class. I create the connection object globally (in a config file) and use Dependency Injection when other objects/functions need it.
Although your procedural approach can be solved pretty easily
function connectDB(){
return mysqli_connect(server, username, password, database);
}
function executeQuery($payload){
static $con;
id (!$con)
{
$con = connectDB();
}
return $con->query($payload);
}
an OOP approach would be indeed better. I am not an OOP pro, but you can take a look at my approach which at least using encapsulation to hide all the dirty job inside and provide concise methods to get the data already in desired format like this:
// to get a username
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
// to get an array of data
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
// to get an array indexed by id field
$data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
// to get a single dimensional array
$ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);
// a simple code for the complex INSERT
$data = array('offers_in' => $in, 'offers_out' => $out);
$sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
$db->query($sql,$pid,$data,$data);
As a solution for your exact problem : "You do not want to instantiate a new MySQL connection for each time a query is executed" ,
Well, we can think about the following :
You need to make your connection variable ($con) in GLOBAL scope, such that when accessed through any function you can grab THAT variable you set before, not instantiate a new one.
we can do this using keyword "global" , as following :
The connection function :
function &connectDB(){
global $con;
if(empty($con)) {
$con = new mysqli(server, username, password, database);
}
return $con;
}
And for more performance , we avoid cloning/copying the connection variable/resource by using reference function ( &connectDB ),
Query Execution function
Now we've set the connection function in a flexible way , to set the queryExecution function , we can use more than one solution :
First solution :
function executeQuery($payload){
$con = &connectDB(); // do not forget the () , it's good practice
return $con->query($payload);
}
In this solution we made use of "reference" , so the expression :
$con = &connectDB();
will set the variable $con as a reference/shortcut for global $con (i.e : just pointing to the global variable $con)
or
Second solution :
function executeQuery($payload){
global $con;
return $con->query($payload);
}
but for the second solution : Function "connectDB()" MUST be called at least once before any calling to "executeQuery()", In order to make sure that there a connection has been established with the database,
Keep in mind that, according to this solution , calling "connectDB()" more than once will not create more than one connection , once it is called , connection is created, if called again it will return the PREVIOUSLY created connection.
Hope it helps :)
by the way : stay with the OOP approach for database connection , it has much more benefits over the procedural ways,
& I recommend using PDO, it is much more portable.
I need to know how many sql queries are being executed per page request. As the site is already done and I am just running optimization analysis, I would prefer if any solution offered doesnt require that i change the entire website structure.
I use a single connection to send all queries to the MySQL database:
define('DATABASE_SERVER', '127.0.0.1');
define('DATABASE_NAME', 'foco');
define('DATABASE_USERNAME', 'root');
define('DATABASE_PASSWORD', '');
$DB_CONNECTION = new mysqli(
DATABASE_SERVER,
DATABASE_USERNAME,
DATABASE_PASSWORD,
DATABASE_NAME,
3306
);
Then to execute a query i use:
$query = "SELECT * FROM `sometable`";
$queryRun = $DB_CONNECTION->query($query);
Is there a way to count how many queries have been sent and log the answer in a text file just before php closes the connection?
You can extend the mysqli object and override the query method:
class logging_mysqli extends mysqli {
public $count = 0;
public function query($sql) {
$this->count++;
return parent::query($sql);
}
}
$DB_CONNECTION = new logging_mysqli(...);
One of the best ways would be to run a log function inside your $DB_CONNECTION->query().
That way you can either log each individual query to a db table, or perform basic test on query speed and then store this, or just increment the count (for number of queries) and store this.
Create class extending mysqli, make $DB_CONNECTION object of that class and do your statistics in your implementation of query() method. Eventually it shall call parent::query() to do real job.
Create a proxy that you use instead of mysqli directly...
class DatabaseCounter {
private $querycount = 0;
private $mysqli = null;
// create mysqli in here in constructor
public function query(...) {
$this->queryCount++;
$this->mysqli->query(...);
}
}
$DB_CONNECTION = new DatabaseCounter();