How can i simplify this php mysql count code and reduce queries? - php

I have database with 8 different product category for download.
pic, app, ebo, tem, des, cod, mus, cat
I'd like to count clients total downloaded products and total downloads of each product category.
Maximum daily limit downloads for category product is 3.
When user log in should see how many downloads remain.
Here is working code
$query = "SELECT COUNT(*) as sum FROM service_downloads where client_id like '$client'";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result))
{
echo "You have downloaded". $row['sum'] ." products.";
echo "<br />";
}
$query = "SELECT COUNT(*) as sum FROM service_downloads where client_id like '$client' and product like 'pic'";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result))
{
echo "". $row['sum'] ." downloaded pictures";
$leftovers = 3 - $row['sum'];
echo " $leftovers pictures remain for download";
echo "<br />";
}
$query = "SELECT COUNT(*) as sum FROM service_downloads where client_id like '$client' and product like 'app'";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result))
{
echo "". $row['sum'] ."downloaded applications";
$leftovers = 3 - $row['sum'];
echo " $leftovers applications remain for download.";
echo "<br />";
}
$query = "SELECT CO.... This procedure repeat eight times for different product category.
result
You have downloaded 12 products.
3 downloaded pictures 0 pictures remain for download.
1 downloaded applications 2 applications remain for download.
3 downl.......

You could use a GROUP BY statement to group your results.
SELECT COUNT(`Product`) AS `Sum`, `Product`
FROM `service_downloads`
WHERE `client_id` = '<client_id>'
GROUP BY `Product`
Then you can use one while statement to loop through each product:
// Print out result
while($row = mysql_fetch_array($result))
{
echo "". $row['Sum'] ."downloaded " . $row['Product'];
$leftovers = 3 - $row['Sum'];
echo " $leftovers " . $row['Product'] . " remain for download.";
echo "<br />";
}

$query = "SELECT COUNT(*) as sum,product FROM service_downloads where client_id like '$client' GROUP BY product";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result))
{
echo "You have downloaded". $row['sum'] ." ".$row['product'];
echo "<br />";
}
This should work

You should breakdown the quantity of downloads per category in one query:
SELECT product,COUNT(*)
FROM service_downloads
WHERE client_id like '$client';
I also don't think you need to use LIKE; you probably want to use =

You can get a single result set with all the sums in it with this query.
SELECT COUNT(*) as sum, product
FROM service_downloads
WHERE client_id = '$client'
AND PRODUCT IN ('pic', 'app', 'abc', 'def', 'ghi')
GROUP BY product WITH ROLLUP
ORDER BY product NULLS FIRST
This will give you one row for each specific product and a summary (rollup) row with a NULL value in the product column.
If this query takes a long time create an index on (client, product) and it should go pretty fast.

If you are showing this data frequently, which is what it sounds like, then you should have a separate table that represents those SUMs and is index by CLIENT_ID.
You can then increment/decrement that value each time you add a new entry.
For example, when you add a new row to service_downloads with an entry in 'pic' for CLIENT_ID 1, then you would also increment this shortcut table:
UPDATE service_counts SET pic=pic+1 WHERE client_id=1;

Related

How to mysqli_fetch_row while using mysqli_multi_query

I'm trying to get the latest info about some specific person, and I'm using a query like
SELECT * FROM Table WHERE Name LIKE 'Peter' ORDER BY ID DESC LIMIT 1
and
SELECT * FROM Table WHERE Name LIKE 'Mary' ORDER BY ID DESC LIMIT 1
because in the Table each day will insert new data for different person at the instant of updating it (for record reference), so I would like to print out a few persons latest info by "ORDER BY ID DESC LIMIT 1"
I have tried to print it out with "mysqli_multi_query" and "mysqli_fetch_row"
like
$con=mysqli_connect($localhost,$username,$password,'Table');
$sql = "SELECT * FROM Table WHERE Name LIKE 'Peter' ORDER BY ID
DESC LIMIT 1 ";
$sql .= "SELECT * FROM Table WHERE Name LIKE 'Mary' ORDER BY ID
DESC LIMIT 1";
// Execute multi query
if (mysqli_multi_query($con,$sql))
{
do
{
// Store first result set
if ($result=mysqli_store_result($con)) {
// Fetch one and one row
while ($row=mysqli_fetch_row($result))
{
echo '<tr>'; // printing table row
echo '<td>'.$row[0].'</td>';
echo '<td>'.$row[1].'</td>';
echo '<td>'.$row[2].'</td>';
echo '<td>'.$row[3].'</td>';
echo '<td>'.$row[4].'</td>';
echo '<td>'.$row[5].'</td>';
echo '<td>'.$row[6].'</td>';
echo '<td>'.$row[7].'</td>';
echo '<td>'.$row[8].'</td>';
echo '<td>'.$row[9].'</td>';
echo '<td>'.$row[10].'</td>';
echo '<td>'.$row[11].'</td>';
echo '<td>'.$row[12].'</td>';
echo '<td>'.$row[13].'</td>';
echo '<td>'.$row[14].'</td>';
echo'</tr>'; // closing table row
}
// Free result set
mysqli_free_result($result);
}
}
while (mysqli_next_result($con));
}
mysqli_close($con);
?>
In the result page , it doesn't show any error message , but no results are printed.
The individual queries were tested.
Please advise, much thanks
Is there another way to keep the query simple, so there is no need to use mysqli_multi_query?
Best practice indicates that you should always endeavor to make the fewest number of calls to the database for any task.
For this reason, a JOIN query is appropriate.
SELECT A.* FROM test A INNER JOIN (SELECT name, MAX(id) AS id FROM test GROUP BY name) B ON A.name=B.name AND A.id=B.id WHERE A.name IN ('Peter','Mary')
This will return the desired rows in one query in a single resultset which can then be iterated and displayed.
Here is an sqlfiddle demo: http://sqlfiddle.com/#!9/2ff063/3
P.s. Don't use LIKE when you are searching for non-variable values. I mean, only use it when _ or % are logically required.
This should work for you:
$con = mysqli_connect($localhost,$username,$password,'Table');
// Make a simple function
function personInfo($con, $sql)
{
$result = mysqli_query($con, $sql);
if(mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_array($result))
{
echo '<table>
<tr>';
for($i=0; $i < count($row); $i++) echo '<td>'.$row[$i].'</td>';
echo '</tr>
</table>';
}
}
}
$sql1 = "SELECT * FROM Table WHERE Name='Peter' ORDER BY ID DESC LIMIT 1 ";
$sql2 = "SELECT * FROM Table WHERE Name='Mary' ORDER BY ID DESC LIMIT 1";
// Simply call the function
personInfo($con, $sql1);
personInfo($con, $sql2);

How to loop for every value in table AND find average

I am a beginner programmer and I am trying to find the average user rating of vendor 2.
My steps are as follows:
1. User 'Jimmy' gives a rating of 3 to vendor 2 (ratings are out of 5)
2.TABLE vendoratings is updated
3. ratingstot.php then calculates the total sum of ratings and the number of responses for ALL vendors before updating TABLE vendortotalratings shown below
4.User 'Jimmy' clicks on 'View average user rating'
5. The values totalratings and totalno are retrieved and divided in Javascript
var average= totalratings/totalno
6. totalno is displayed to user Jimmy. END
Question
1. I need help forming the for or while loop in ratingstot.php to calculate both ratings and no. of responses belonging to vendor X before inserting them in vendortotalratings table for every vendor.
ratingstot.php
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
error_reporting(E_ERROR);
try{
//Database connection
$conn = new mysqli("localhost", "XXXXXXXX_XXX", "XXXXXXXX", "XXXXXXXX");
//Unsure how to loop this to make vendor new value every loop
for($i=0; $i<=6; $i++){
$vendor = ??
//Calculate sum of ratings from table ratings
$result = $conn->query("SELECT SUM(ratings) FROM ratings WHERE vendorid = '".$vendorid."' ");
$row = mysqli_fetch_array($result);
$totalratings = $row[0];
//Calculate no. of responses (by counting no. of rows)
$result1 = $conn->query("SELECT * FROM ratings WHERE vendorid = '" . $vendorid."' ");
$totalno = mysqli_num_rows($result);
//inserting the results into the table
$query = " UPDATE vendortotalratings SET ";
$query .= " totalratings = '". $totalratings ."', totalno='".$totalno."' ";
$query .= " WHERE vendorid = '". $vendorid ."'";
$result2 = $conn->query($query);
}
echo($outp);
}
catch(Exception $e) {
$json_out = "[".json_encode(array("result"=>0))."]";
echo $json_out;
}
?>
I have no idea how to loop this, are there any easier steps to calculate average of ratings for each vendors?
Instead of all these complicated things, you can simplify the required solution like this:
(Assumption: I'm assuming that vendorid, ratings, totalratings and totalno columns are of type INT)
Use the below statement/query to get the totalratings and totalno corresponding to each vendorid.
$result = $conn->query("SELECT vendorid, SUM(ratings) as totalratings, COUNT(userid) as totalno FROM vendorratings GROUP BY vendorid");
Now loop through the $result result set using while() loop.
while($row = $result->fetch_array()){
...
}
In each iteration of above while() loop, check if the vendorid value already exists or not. If it exists, then UPDATE the row with new totalratings and totalno, otherwise INSERT a new row comprising of vendorid, totalratings and totalno.
while($row = $result->fetch_array()){
$res = $conn->query("SELECT vendorid FROM vendortotalratings WHERE vendorid = " . $row['vendorid']);
if($res->num_rows){
// Update the existing row
$conn->query("UPDATE vendortotalratings SET totalratings = ".$row['totalratings'].", totalno = ".$row['totalno']." WHERE vendorid = ".$row['vendorid']);
echo "Affected rows: " . $conn->affected_rows . '<br />';
}else{
// Insert a new row
$res = $conn->query("INSERT INTO vendortotalratings VALUES(".$row['vendorid'].", ".$row['totalratings'].",".$row['totalno'].")");
if($res) echo "New row inserted <br />";
}
}
So the complete code of try-catch block would be like this:
// your code
try{
//Database connection
$conn = new mysqli("localhost", "XXXXXXXX_XXX", "XXXXXXXX", "XXXXXXXX");
$result = $conn->query("SELECT vendorid, SUM(ratings) as totalratings, COUNT(userid) as totalno FROM vendorratings GROUP BY vendorid");
while($row = $result->fetch_array()){
$res = $conn->query("SELECT vendorid FROM vendortotalratings WHERE vendorid = " . $row['vendorid']);
if($res->num_rows){
// Update the existing row
$conn->query("UPDATE vendortotalratings SET totalratings = ".$row['totalratings'].", totalno = ".$row['totalno']." WHERE vendorid = ".$row['vendorid']);
echo "Affected rows: " . $conn->affected_rows . '<br />';
}else{
// Insert a new row
$res = $conn->query("INSERT INTO vendortotalratings VALUES(".$row['vendorid'].", ".$row['totalratings'].",".$row['totalno'].")");
if($res) echo "New row inserted <br />";
}
}
}catch(Exception $e) {
$json_out = json_encode(array("result"=>0));
echo $json_out;
}
The question doesn't clarify how the records are first inserted in vendortotalratings table. So, assuming that there is already a record in this table for each vendor, you don't have to write a whole new loop.
Updating vendortotalratings:
SQL can take care of calculating the total ratings and their counts in a single query which can then replace the loop that you have.
UPDATE vendortotalratings vtr
INNER JOIN
(
SELECT vendorid, SUM(ratings) AS sumratings, COUNT(ratings) AS countratings
FROM vendoratings
GROUP BY vendorid
) vr
ON vtr.vendorid = vr.vendorid
SET
vtr.totalratings = vtr.totalratings + vr.sumratings
,vtr.totalno = vtr.totalno + vr.countratings
Computing averages:
As for your second question, to compute the average, you could run the following query which will give you the run-time result:
SELECT vendorid, totalratings, totalno,
CAST((totalratings/totalno) AS DECIMAL(10, 2)) AS avgrating
FROM vendortotalratings;
The variable avgrating can be accessed directly in PHP by using $row['avgrating'] if you're fetching an associative array from the results, or by using the appropriate index number, which in this case should be $row[3]

PHP display id with the same value in a column

Hello so I have this table with some equal values
And this is my query so far
$stmt = $dbc->query("SELECT weight, GROUP_CONCAT(id) AS ids FROM tbl_weight GROUP BY weight HAVING COUNT(*) > 1");
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['ids'] . "<br />";
}
And the output when I run this query is
What I actually want to get is like this
How is this possible?

Fixing performance issues when looping through MySQL results in PHP

I'm working on a virtual game board style game in which players get points on a certain area of the board. (Take it easy on me now as I only do this as a hobby so I might be doing this in the worst way possible)
I have 3 tables. One stores all the player information (eg. id, screenname). A second stores all the Area information (eg. id, x, y) and a third stores how many points each player has in each area (eg. id, playerid, areaid, points). In order to create a "leaderboard" I'm looping through all the players, then within that loop I'm also looping through all the areas, then finally within that second loop, I get the leader of that area and see if that matches the current player in the first loop, if so I increment a counter, then store it into an array. (See code below with some commenting)
I looked into MySQL caching, but I dont have access to a lot of the server options, as well as would like to keep as much of the results as live as possible, so caching may not be the right way to go.
My question is whether or not I'm doing this properly. Currently there is only around 10 players, and approx. 500 areas. I'm finding the below script already takes about 5-8 seconds to run. Potentially there could be millions of areas, so such a long delay in processing could be catastrophic (for the leaderboard anyway). Am I going about this the right way, and/or is there a better way to do this?
<?php
$leaders = array();
//Loop through all the players
$sql = "SELECT * FROM players";
$result = mysqli_query($con, $sql) or die(mysqli_error($con));
while ($row = mysqli_fetch_array($result)) {
//save player information into variables
$playerId = $row['id'];
$playerScreenName = $row['screenname'];
//Reset the area counter
$AreaCount = 0;
$leader = array();
//Loop through all areas
$sql2 = "SELECT * FROM areas";
$result2 = mysqli_query($con, $sql2) or die(mysqli_error($con));
while ($row2 = mysqli_fetch_array($result2)) {
$areaId = $row2['id'];
//Get the player with the most points in that area
$sql3 = "SELECT * FROM points WHERE areaid='$areaId' ORDER BY totalpoints DESC LIMIT 1";
$result3 = mysqli_query($con, $sql3) or die(mysqli_error($con));
while ($row3 = mysqli_fetch_array($result3)) {
$leaderOfArea = $row3['playerid'];
//See if the leader of the area is the same player we are looping through
if ($playerId == $leaderOfArea) {
//if it is, then increment the counter
$AreaCount++;
}
}
}
//Store the leader information into an array to be output later
$leader['screenname'] = $playerScreenName;
$leader['areacount'] = $AreaCount;
$leaders[] = $leader;
}
// sort leaders by score
usort($leaders, 'compare_areacount');
?>
There is overhead of opening database connections, and when you do it in a loop, you exacerbate the problem (and then when you add a loop inside of that, you make it that much worse). Instead, restructure it as one query using a Join or Subquery.
This post has some specifics.
I think this code can help. You might have to change the sql queries with appropriate column names you need.
<?php
$leaders = array();
//Loop through all the players
$sql = "SELECT * FROM players";
$result = mysqli_query($con, $sql) or die(mysqli_error($con));
$players = array();
while ($row = mysqli_fetch_array($result)) {
//save player information into variables
$players[$row['id']] = array($row['screenname'], 0);
// number 0 will be the count of how many times this player is the leader
}
$sql = "SELECT Area.id, Area.name, (SELECT Pts.playerid FROM `points`"
. " AS Pts WHERE Pts.areaid=Area.id ORDER BY totalpoints"
. " DESC LIMIT 1) AS `leader_id` FROM `areas` AS Area";
$result = mysqli_query($con, $sql) or die(mysqli_error($con));
$areas = array();
while ($row = mysqli_fetch_row($result)) {
$areas[$row[0]] = $row;
// $row[2] will contain leader_id
// index 1 corresponds to the second element in player values array
$players[$row[2]][1]++;
}
// now if you want to print:
foreach ($areas as $area_id => $area) {
echo "Area id: " . $area_id . ", name: " . $area[1] . ", leader_id: " . $area[2] . "<br /><br />";
}
foreach ($players as $player_id => $player) {
echo "Player id: " . $player_id . ", name: " . $player[0] . ", No of areas this player is a leader of: " . $player[1] . "<br /><br />";
}

MySQL/PHP Count

I have 2 MySQL tables, one for storing albums and the other for songs. I am displaying a list of albums in a table, and I want to be able to add a column called songs to display the number of songs in this album. The way I have it now just messes up my table:
Thanks for everyone's help!
Assuming that "playlist" is what you consider an album and the first while loop iterates on playlists, I'd rewrite your code like this:
while($row = mysql_fetch_array($rs)) {
echo "<tr>\n";
echo "<td>".$row['playlist_id']."</td>";
// Assuming playlist_id is an integer value in your database
$query = "
SELECT Playlist_id, COUNT(Playlist_id) AS songCount
FROM ws_music
WHERE Playlist_id = ". intval ($row['playlist_id']) ."
GROUP BY Playlist_id
";
$result = mysql_query($query) or die(mysql_error());
// No need for the second while loop
$row2 = mysql_fetch_array($result);
echo "<td>There are ". $row2['songCount'] ." ". $row2['Playlist_id'] ." song.</td>";
echo "</tr>";
}

Categories