I am using PDO and trying to display how many users are in a table, however I get this error:
Notice: Array to string conversion in C:\Users\admin.phtml on line 10 Array
Then in my Admin class:
public function CountUsers()
{
$query = "SELECT * FROM Users";
$statement = $this->_dbHandle->prepare($query);
// Then execute the query
$statement->execute();
// This will return the row from the database as an array
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
Then in my Admin Controller I have:
$Admin = new Admin();
$CountUsers = $Admin->CountUsers();
To display the results I have this in my phtml:
<var><?php echo $CountUsers; ?></var>
Does anyone understand what I am doing wrong?
You should try to count the users in query, like so:
public function CountUsers() {
$query = "SELECT COUNT(1) FROM Users";
$statement = $this->_dbHandle->prepare($query);
if($statement->execute()) {
// This will return COUNT(1) field
return $statement->fetch(PDO::FETCH_NUM)[0];
}else{
return 0; // should the query fail, return 0
}
}
If you want to stick to counting in PHP (which you shouldn't), modify your code to do:
return count($statement->fetchAll(PDO::FETCH_ASSOC));
But you shouldn't query all of the data just to count and discard it. Using SQL's count is far more performant for several reasons:
You don't fetch the actual data. If your data set is big, you have to transfer megabytes or gigabytes over sockets. Which is not too good idea, even if the database is on the same machine.
You don't store the data in PHP. When using fetchAll, it will store all of the data into PHP array, taking up memory
MySQL engine might use optimisations which further speed things up (looking up the record count directly)
To display a meaningful text, or do something with this function you can:
function CheckUsers() {
$userCount = CountUsers();
if($userCount == 0) {
echo 'No users found';
}else{
echo $userCount . ' users found';
}
}
Related
I'm trying to return the result of 3 tables being joined together for a user to download as CSV, and this is throwing the error:
Allowed memory size of 734003200 bytes exhausted
This is the query being run:
SELECT *
FROM `tblProgram`
JOIN `tblPlots` ON `tblPlots`.`programID`=`tblProgram`.`pkProgramID`
JOIN `tblTrees` ON `tblTrees`.`treePlotID`=`tblPlots`.`id`
The line of code causing the error is this:
$resultsALL=$this->db->query($fullQry);
Where $fullQry is the query shown above. When I comment out that single line, everything runs without the error. So I'm certain its not an infinite loop somewhere I'm missing.
I'm wondering how do I break up the query so that I can get the results without erroring out? The tables only have a relatively small amount of data in them right now and will be even larger eventually, so I don't think raising the memory size is a good option.
I'm using CodeIgniter/php/mysql. I can provide more code if need be...
Thank you for any direction you can advise!
Based off of: MySQL : retrieve a large select by chunks
You may also try retrieving the data in chunks by using the LIMIT clause.
Since you're using CodeIgniter 3, here is how you can go about it.
You may need to pass a different $orderBy argument#6 to the getChunk(...) method if in case your joined tables have conflicting id column names.
I.e: $this->getChunk(..., ..., ..., 0, 2000, "tblProgram.id");
Solution:
<?php
class Csv_model extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->database();
}
public function index()
{
$sql = <<< END
SELECT *
FROM `tblProgram`
JOIN `tblPlots` ON `tblPlots`.`programID`=`tblProgram`.`pkProgramID`
JOIN `tblTrees` ON `tblTrees`.`treePlotID`=`tblPlots`.`id`
END;
$this->getChunk(function (array $chunk) {
/*
* Do something with each chunk here;
* Do something with each chunk here;
* log_message('error', json_encode($chunk));
* */
}, $this->db, $sql);
}
/*
* Processes a raw SQL query result in chunks sending each chunk to the provided callback function.
* */
function getChunk(callable $callback, $DBContext, string $rawSQL = "SELECT 1", int $initialRowOffset = 0, int $maxRows = 2000, string $orderBy = "id")
{
$DBContext->query('DROP TEMPORARY TABLE IF EXISTS chunkable');
$DBContext->query("CREATE TEMPORARY TABLE chunkable AS ( $rawSQL ORDER BY `$orderBy` )");
do {
$constrainedSQL = sprintf("SELECT * FROM chunkable ORDER BY `$orderBy` LIMIT %d, %d", $initialRowOffset, $maxRows);
$queryBuilder = $DBContext->query($constrainedSQL);
$callback($queryBuilder->result_array());
$initialRowOffset = $initialRowOffset + $maxRows;
} while ($queryBuilder->num_rows() === $maxRows);
}
}
Use getUnbufferedRow() for processing large result sets.
getUnbufferedRow()
This method returns a single result row without prefetching the whole
result in memory as row() does. If your query has more than one row,
it returns the current row and moves the internal data pointer ahead.
$query = $db->query("YOUR QUERY");
while ($row = $query->getUnbufferedRow()) {
echo $row->title;
echo $row->name;
echo $row->body;
}
For use with MySQLi you may set MySQLi’s result mode to
MYSQLI_USE_RESULT for maximum memory savings. Use of this is not
generally recommended but it can be beneficial in some circumstances
such as writing large queries to csv. If you change the result mode be
aware of the tradeoffs associated with it.
$db->resultMode = MYSQLI_USE_RESULT; // for unbuffered results
$query = $db->query("YOUR QUERY");
$file = new \CodeIgniter\Files\File(WRITEPATH.'data.csv');
$csv = $file->openFile('w');
while ($row = $query->getUnbufferedRow('array'))
{
$csv->fputcsv($row);
}
$db->resultMode = MYSQLI_STORE_RESULT; // return to default mode
Note:
When using MYSQLI_USE_RESULT all subsequent calls on the same
connection will result in error until all records have been fetched or
a freeResult() call has been made. The getNumRows() method will
only return the number of rows based on the current position of the
data pointer. MyISAM tables will remain locked until all the records
have been fetched or a freeResult() call has been made.
You can optionally pass ‘object’ (default) or ‘array’ in order to
specify the returned value’s type:
$query->getUnbufferedRow(); // object
$query->getUnbufferedRow('object'); // object
$query->getUnbufferedRow('array'); // associative array
freeResult()
It frees the memory associated with the result and deletes the result
resource ID. Normally PHP frees its memory automatically at the end of
script execution. However, if you are running a lot of queries in a
particular script you might want to free the result after each query
result has been generated in order to cut down on memory consumption.
$query = $thisdb->query('SELECT title FROM my_table');
foreach ($query->getResult() as $row) {
echo $row->title;
}
$query->freeResult(); // The $query result object will no longer be available
$query2 = $db->query('SELECT name FROM some_table');
$row = $query2->getRow();
echo $row->name;
$query2->freeResult(); // The $query2 result object will no longer be available
I am currently busy on a textbased RPG game, but I am stuck at one part right now.
In order to start a mission, the player does need some items, these are stored in a string: item:1x3-item:5x1 - (basicly item:IDxamount).I have already made a function that explodes the string into variables, but now the script needs to check if the player does have all the items listed.
I've tried to solve the issue with a foreach, but that returns positive or negative for every item, and I only need to know if the player has all items at once.
(don't mind the unsafe query)
$parseAmount is an array, containing all item ID's.
$uid is an variable containing userID
// check if player has all items
foreach($parseAmount as $itemID)
{
$check_query = mysql_query("SELECT * FROM `player_items` WHERE `player`='$uid' AND `item`=='$itemID' AND `value`>='$parseAmount[1]'");
if(mysql_num_rows($check_query)>=1)
{return true;}
else
{return false;}
}
If you want me to post the whole function, please let me know.
If I understood your question correctly you need something like:
foreach($parseAmount as $itemID) {
$sql = "SELECT COUNT(*) AS count
FROM player_items
WHERE player = '".mysql_real_escape_string($uid)."'
AND item = '".mysql_real_escape_string($itemID)."'
AND value >= ".intval($parseAmount[1]);
$row = mysql_fetch_array(mysql_query($sql));
if ($row['count'] == 0) {
return false;
}
}
return true;
You must not early return true. You know the result is true only after checking all the items. My code could be improved by selecting all the items at once, but it's up to you to build this.
Keep in mind my comment about the deprecation of the MySQL extension, using MySQLi and Prepared Statements it will look something like this (note that I never worked with MySQLi before and built it with help of the manual):
foreach($parseAmount as $itemID) {
$sql = "SELECT COUNT(*) AS count
FROM player_items
WHERE player = ?
AND item = ?
AND value >= ?"
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ssi", $uid, $itemID, $parseAmount[1]);
$stmt->execute();
$row = $stmt->get_result()->fetch_array();
if ($row['count'] == 0) {
return false;
}
}
return true;
Here is a simple scenario I am contemplating. I have a query that is executed on a MySQL database from PHP. I would like to check if any data was returned from the query. However, in order to perform that check, it pulls out one of the rows of returned data.
Look at this example, and its comments:
$booksGrabber = ("SELECT * FROM table");
if (!$booksGrabber) {
//Query failed, perhaps a syntax error
exit;
}
if (!mysql_result($booksGrabber, 0)) {
//No data was returned :(
exit;
}
while ($book = mysql_fetch_assoc($booksGrabber)) {
//mysql_result() stole the first row of returned data
//So if I was expecting the loop to display 4 results,
//I only get 3...
}
How can I check if data was returned from the database without running two queries (one to check, the other to display all of the data) or having one of the rows stolen?
Use mysql_num_rows. It was as simple as it looked.
EDIT: If you want more complicated, add mysql_data_seek($booksGrabber,0).
Simple: mysql_query() function that runs the query will return false on error and then you can use mysql_num_rows() for the count. So you can do something like:
$result = mysql_query('SELECT * FROM table');
if (!$result) {
print('Invalid query: ' . mysql_error());
} elseif (mysql_num_rows() == 0) {
print("no results");
} else {
// You can use safely data from the query :)
}
I write a lot of SELECT * FROM... kind of queries in my web sites. I'd like to write a function that looks after this for me so I can call on it more quickly, without using more advanced techniques like PDO and OOP. Im just confused on how I would call the data I retrieve from the database, particularly when looping through the array's results.
I'd love something like this:
function selectAll($tableName, $limitAmount) {
global $dbConnection;
$query = mysql_query("SELECT * FROM $tableName ORDER BY id LIMIT $limitAmount");
$row_result = mysql_fetch_assoc($query);
return $row_result;
}
Say it was a bunch of news posts. Id like to loop through the results in one of the typical ways:
// CALL THE FUNCTION
selectAll('news_table', '10');
// SOMEHOW LOOP THROUGH RESULTS??
do {
echo "<h2>".$row_result['title']."</h2>";
} while ($row_result = mysql_fetch_assoc($query));
Obviously this isn't how I loop through the bespoke results of a function. Im not even sure if my function is correct.
Any help is greatly appreciated.
EDIT: Forgot to return a result inside the function and call the actual function. My bad. Updated now.
There is no point in having such a function called like yours.
Just make it like this
function fetchAll($query) {
$res = mysql_query($query) or trigger_error("db: ".mysql_error()." in ".$query);
$a = array();
if ($res) {
while($row = mysql_fetch_assoc($res)) $a[]=$row;
}
return $a;
}
and use it with whatever query:
$data = fetchAll("SELECT * FROM news_table ORDER BY id LIMIT 10");
foreach ($data as $row) {
echo $row['title'];
}
An SQL query being a powerful program itself. Do not reduce it's power to silly selects.
Use SQL to represent data processing logic and this helper function to avoid repetitions.
I'm using PHP ADOdb and I can get the result set:
$result = &$db->Execute($query);
How do I get the field names from that one row and loop through it?
(I'm using access database if that matters.)
It will depend on your fetch mode - if you setFetchMode to ADODB_FETCH_NUM (probably the default) each row contains a flat array of columns. If you setFetchMode to ADODB_FETCH_ASSOC you get an associative array where you can access each value by a key. The following is taken from ADODB documentation - http://phplens.com/lens/adodb/docs-adodb.htm#ex1
$db->SetFetchMode(ADODB_FETCH_NUM);
$rs1 = $db->Execute('select * from table');
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1')
To loop through a set of results:
$result = &$db->Execute($query);
foreach ($result as $row) {
print_r($row);
}
Small improvement to the solution posted by #thetaiko.
If you are ONLY needing the field names, append LIMIT 1 to the end of your select statement (as shown below). This will tell the server to send you a single row with column names, rather than sending you the entire table.
SELECT * FROM table LIMIT 1;
I'm working with a table that contains 9.1M records, so this minor change speeds up the query significantly!
This is a function I use to return a field array - I've stripped out some extra stuff that, for example, allows it to work with other DBs than MySQL.
function getFieldNames($strTable, $cn) {
$aRet = array();
# Get Field Names:
$lngCountFields = 0;
$strSQL = "SELECT * FROM $strTable LIMIT 1;";
$rs = $cn->Execute($strSQL)
or die("Error in query: \n$strSQL\n" . $cn->ErrorMsg());
if (!$rs->EOF) {
for ($i = 0; $i < $rs->FieldCount(); $i++) {
$fld = $rs->FetchField($i);
$aRet[$lngCountFields] = $fld->name;
$lngCountFields++;
}
}
$rs->Close();
$rs = null;
return $aRet;
}
Edit: just to point out that, as I say, I've stripped out some extra stuff, and the EOF check is therefore no longer necessary in the above, reduced version.
I initally tried to use MetaColumnNames, but it gave differing results in VisualPHPUnit and actual site, while running from the same server, so eventually
I ended up doing something like this:
$sql = "select column_name, column_key, column_default, data_type, table_name, table_schema from information_schema.columns";
$sql .= ' where table_name="'.$table.'" and table_schema="'.$database_name.'"';
$result = $conn->Execute($sql);
while($row = $result->fetchRow()) {
$out[] = strToUpper($row['column_name']);
}
I think it should work with mysql, mssql and postgres.
The benefit of doing it like this, is that you can get the column names, even if a query from a table returns an empty set.
If you need the Coloumn names even for empty tables or for joins about multiple tables use this:
$db->Execute("SELECT .......");
// FieldTypesArray - Reads ColoumnInfo from Result, even for Joins
$colInfo = $res->FieldTypesArray();
$colNames = array();
foreach($colInfo as $info) $colNames[] = $info->name;
The OP is asking for a list of fieldnames that would result of executing an sql statement stored in $query.
Using $result->fetchRow(), even with fetch mode set to associative, will return nothing if no records match the criteria set by $query. The $result->fields array would also be empty and would give no information for getting the fieldnames list.
Actually, we don't know what's inside the $query statement. Besides, setting limit to 1 may not compatible with all database drivers supported by PHP ADOdb.
Answer by Radon8472 is the right one, but the correct code could be:
$result = $db->Execute($query);
// FieldTypesArray - an array of ADOFieldObject Objects
// read from $result, even for empty sets or when
// using * as field list.
$colInfo = [];
if (is_subclass_of($result, 'ADORecordSet')){
foreach ($result->FieldTypesArray() as $info) {
$colInfo[] = $info->name;
}
}
I have the habit of checking the class name of $result, for as PHP ADOdb will return false if execution fails.