MYSQLi Count and Group by - php

I have this table (sample)
|---id---|---place---|---admin---|
| 1 | Q | AJ |
| 2 | p | PM |
| 3 | w | AJ |
| 4 | t | TY |
| 5 | u | AJ |
I want to count how many places each admin control using MYSQLi not mysql.
I have searched and all what I found is MYSQL and using COUNT(*) and when I am trying to use it, does not work.
Thanks all in advance.

$result = mysqli_query("SELECT admin, COUNT(admin) as 'amount' FROM tablename GROUP BY admin");
while($row = mysqli_fetch_assoc($result))
echo "Name: " . $row['admin'] . " - ". $row['amount'] . "<br />";
Using count + GROUP by you can get the amount of times each admin appears in the table.

Related

Mysql query to join two table and group by matching id

Am trying to group user orders by the order id, but am not getting it right i know this will be first get done in the SQL query then organise it well in PHP and HTML but i don't know how to get it done.
orderinfo
oid | userid | total | payid
-----|---------|--------|----------
oi10 | peter | 650 | VCC-100
oi12 | john | 30 | VCC-500
oi15 | peter | 60 | COD-500
itemorder
pid | ioid | userid | price | qty | itemname
----| ------|---------|--------|-----|-----------
p10 | oi10 | peter | 200 | 1 | lexus
p20 | oi10 | peter | 150 | 1 | Toyota
p15 | oi10 | peter | 300 | 1 | Myvi
p66 | oi15 | peter | 25 | 2 | BMW
p67 | oi15 | peter | 10 | 1 | Saga
p67 | oi12 | john | 10 | 3 | Saga
My current Code
$handler->prepare('
SELECT * FROM itemorder io
LEFT JOIN orderinfo oi
ON io.oid = oi.ioid
WHERE io.userid = 'peter'
GROUP BY io.oid
ORDER BY oi.pid DESC
');
$handler->execute();
$RecentOrder = $handler->getAll();
$handler->free();
if(!empty($RecentOrder)){
foreach($RecentOrder as $row){
}
}
Expected Result
I want the result to be sorted according to the order id all item that has same order id will be listed (list according to order id).
oid: oi10
--------
lexus
Toyota
Myvi
---------------------
oid: oi15
--------
BMW
Saga
The desired output can be retrieved with just ORDER BY.
SELECT *
...
ORDER BY oi.oid DESC, io.pid DESC
And then do the specific formatting in PHP. Easiest way is probably to remember the order_id of the last row.
$lastOrderId = null;
foreach ($result as $row) {
if ($lastOrderId != $row['oid']) {
echo 'oid: ' . $row['oid'] . PHP_EOL;
echo ' -----------' . PHP_EOL;
}
echo ' ' . $row['itemname'] . PHP_EOL;
$lastOrderId = $row['oid'];
}
You can try this :
SELECT io.ioid,GROUP_CONCAT("",io.itemname) as order_items FROM itemorder as io
LEFT JOIN orderinfo as oi
ON io.ioid = oi.oid
WHERE io.userid = 'peter'
GROUP BY io.ioid
ORDER BY io.pid DESC
Please not the columns on which join is done.
Then in PHP you can use explode function to get the array of names for each order.
Hope this helps!

A way around having to loop through results to access more information using the first result set

Given the following Database setup:
Customers
| customerId | accountId | addressId |
|------------|-----------|-----------|
| 1 | 110 | 8 |
| 2 | 120 | 9 |
| 3 | 130 | 10 |
Address
| addressId | companyName | state |
|-----------|-------------|-------|
| 8 | FooBar Co | FL |
| 9 | Self Co | VA |
| 10 | Cli Co | CA |
Tests
| testId | accountId | testType | Status |
|--------|-----------|----------|---------|
| 1 | 120 | Urine | Done |
| 2 | 110 | Blood | Pending |
| 3 | 110 | Blood | Pending |
| 4 | 130 | Biopsy | Done |
| 5 | 130 | Blood | Done |
| 6 | 130 | Urine | Pending |
| 7 | 110 | Biopsy | Pending |
| 8 | 120 | Urine | Pending |
| 9 | 110 | Biopsy | Pending |
| 10 | 110 | Urine | Pending |
Is there a way for me to get around having to loop through a mysqli result set of customers in order to grab the COUNT of each testType based on the name of the type of test and based on the accountId. So for example I am currently doing this:
$sql = "SELECT C.accountId, A.companyName
FROM Customers C
JOIN Address A
ON C.addressId=A.addressId";
$result = mysqli_query($connection, $sql);
$customers = mysqli_fetch_all($result, MYSQLI_ASSOC);
foreach( $customers as $customer ) :
echo '<h2>' . $customer["companyName"] . '</h2>';
// Urine Tests Count
$sql = 'SELECT COUNT(testId) as count FROM Tests WHERE accountId=' . $customer["accountId"] . ' AND testType="Urine"';
$result = mysqli_query($connection, $sql);
$testCount = mysqli_fetch_array($result, MYSQLI_ASSOC);
echo '<p>Total Urine Tests: ' . $testCount["count"] . '</p>';
// Biopsy Tests Count
$sql = 'SELECT COUNT(testId) as count FROM Tests WHERE accountId=' . $customer["accountId"] . ' AND testType="Biopsy"';
$result = mysqli_query($connection, $sql);
$testCount = mysqli_fetch_array($result, MYSQLI_ASSOC);
echo '<p>Total Biopsy Tests: ' . $testCount["count"] . '</p>';
// Blood Tests Count
$sql = 'SELECT COUNT(testId) as count FROM Tests WHERE accountId=' . $customer["accountId"] . ' AND testType="Blood"';
$result = mysqli_query($connection, $sql);
$testCount = mysqli_fetch_array($result, MYSQLI_ASSOC);
echo '<p>Total Blood Tests: ' . $testCount["count"] . '</p>';
endforeach;
As you can probably tell this is hella repetative but so far it's been the only way to get the information I want. I know I could do a COUNT() with a GROUP BY for the tests once I am inside the foreach loop, but if I do that, I won't get back 0 for tests that customer might not have and I need to display if a testType has 0.
I am a lot more well versed in PhP then I am in MySQLi and SQL. Is there something I am missing here? Surely there has to be a better way to get all of this, maybe even get it in one query? Any suggestions or a point in the right direction would be great.
Simple aggregation and a left join or two on your main query should do it. A cross join could be used to identify the unique test types and by cross joining this to a customer/address 1:1 relationship we generate a row for each test type for every customer. That way each customer will have one of each type listed with the appropriate count (0) when no tests of that type exist for an account.
SELECT C.accountId, A.companyName, Z.TestType, Count(distinct T.TestID)
FROM Customers C
LEFT JOIN Address A
ON C.addressId=A.addressId
CROSS JOIN (Select Distinct TestType from Tests) Z
LEFT JOIN Tests T
on T.AccountID = C.AccountID
and Z.TestType = T.TestType
and ReceiveDT>=SomeDate
and ReceiveDT<=SomeOtherDate
GROUP BY C.accountId, A.companyName, Z.TestType
LEFT Joinn will return all customer records and only those with address that match and only records from test matching a customer record. So count will be 0 when no type/test match.
Customer:Address:TestTypes:Tests cardinality expected: 1:1:M:M
so if there were only 3 types of tests I would expect to see 3* the number of records in customers in the results. This is because Address and customers appear to be a 1:1 the number of test types will determine how many times we replicate a customer record and the left join to tests is being aggregated so it will not add to the row count.
Obviously you can add a where clause if you need to limit to a specific customer, but I thought you wanted all customers's test type counts for all possible test types.

JOIN vs multiple queries when there is LIMIT 5

I have two tables like below:
// posts
+----+---------+------------------+-----------+
| id | title | content | author_id |
+----+---------+------------------+-----------+
| 1 | title1 | content1 | 123 |
| 2 | title2 | content2 | 456 |
| . | . | . | . |
| . | . | . | . |
| . | . | . | . |
+----+---------+------------------+-----------+
// users
+----+-------+
| id | name |
+----+-------+
| 1 | Jack |
| 2 | Peter |
| . | . |
| . | . |
| . | . |
+----+-------+
I want to select 5 posts like below:
SELECT * FROM posts WHERE 1 ORDER BY id LIMIT 5
I also need to get the name of author for each post. So I can do that by using a JOIN like this:
SELECT p.*, u.name
FROM posts p
JOIN users ON p.author_id = u.id
WHERE 1
ORDER BY id
LIMIT 5
I can do that the other way like this:
SELECT * FROM posts WHERE 1 ORDER BY id LIMIT 5
// storing results in a PHP array named $results
foreach( $results as $result ) {
SELECT name FROM users WHERE id = $result['author_id']
// storing results in a PHP array named $names
}
// combining $names and $results to make expecting result
So, which approach is more efficient for huge dataset? In other word, Does JOIN happen before LIMIT? if yes, then I guess doing that by PHP would be faster, am I wrong?
An engine can specify its own implementation, but MySQL will not read through the entire table: it will do LIMIT at the same time as the JOIN, fetching line by line until it has found 5 lines.
MySQL Engine: InnoDB uses the row level locking in table then LIMIT will work at the same time as the JOIN, fetching line by line until it has found 5 lines.

Sorting an array of information from another array output from SQL

I need to be retrieve multiple unique values from an array set of data. Currently they are extracted as follows :
while($row1 = mysql_fetch_array($result1))
{
echo "<tr>".
"<td>".$row1[0] . "</td>".
"<td>".$row1[1] . "</td>".
"<td>".$row1[2] . "</td>".
"<td>".$row1[3] . "</td>".
"<td>".$row1[4] . "</td>".
"<td>".$row1[5] . "</td>".
"<td>".$row1[6] . "</td>".
"<td>".$row1[7] . "</td>".
//$row1[8] is the number of hours
"<td>".$row1[8] . "</td>".
//$row1[9] is the user
"<td>".$row1[9] . "</td>";
}
As commented above, I need to accumulate the hours per user. However, I have problem sorting the array as the user value has to be unique in the array whereas the number has to keep stacking.
Really confused now. Appreciate any help offered.
EDIT : $row1[8] is an integer yes.
The sample data output table ( sorry no image) will be as follows :
------------------------------------------------------------------------------------------
Users |Telephone | Address | Postal Code | Hobbies | Interest| FB |Twitter | Insta | Hours
------------------------------------------------------------------------------------------
John | 92238726 | SG | 345322 | Running | Movies | 1 | 0 | 0 | 5
Tom | 922382134 | MY | 345212 | Soccer | Movies | 1 | 0 | 0 | 8
Jerry | 92238726 | SG | 342122 | stamps | Nil | 0 | 1 | 0 | 5
John | 92238726 | SG | 345322 | Running | Movies | 1 | 0 | 0 | 12
Jerry | 92238726 | SG | 342122 | stamps | Nil | 0 | 1 | 0 | 2
Based on the output above which was extracted with the mysql_fetch_array, I'd like to sort the information to something like the following :
Users | Total Hours
John | 17
Tom | 8
Jerry | 7
SQL CODE :
"select DISTINCT jl.refno, jl.logno, jl.attendee, jl.jobsummary, FROM_UNIXTIME(jl.date, '%d/%m/%y') AS 'Date-In', from_unixtime(jl.dateout + (15*3600), '%d/%m/%y') AS 'Date-Out', #timein := (left(jl.timein,2)*60+right(jl.timein,2)) AS 'Time-In', #timeout := (left(jl.timeout,2)*60+right(timeout,2)) AS 'Time-Out', #temp := ((dateout -date)* 24 * 60) + #timeout - #timein AS 'temp', us.username from joblog jl, projects proj, users us where jl.project ='{$value}' AND proj.id ='{$value}' AND jl.staff = us.id"
Since you are using MySQL anyway, I recommend you to do it with an SQL. It's cleaner.
Edit your query to:
SELECT users, SUM(hours) as total FROM userTable GROUP BY users ORDER BY total DESC;
UPDATE - PHP Edition:
You can do it like this in PHP.
$counters = array();
while ($row1 = mysql_fetch_array($result1)) {
$counters[$row1[9]] += $rows1[8];
}
arsort($counters);
foreach ($counters as $name => $total) {
// do your output here
}
<?php
function totalHours($user){
$result=mysql_query("select sum(hours) as total from table_name where users='".$user."'");
$row=mysql_fetch_array($result);
return $row['total'];
}
$result=mysql_query("select * from table_name group by users");
echo "</table>";
echo "<tr>
<td>User</td>
<td>Total Hours</td>
</tr>";
while($row1 = mysql_fetch_array($result1))
{
echo "<tr>".
"<td>".$row1[0]. "</td>".
"<td>".totalHours($row1[0]). "</td></tr>";
}
echo "</table>";
?>

creating a specific query using mysql and php

I need help creating a specific query, The following is an example of my deposit table, empId is a foreign key that refers to the primary key of my 'users' table which is 'userId'
Note:users table is not shown here
mysql> SELECT * FROM deposit
-> ;
+------------+---------------+---------+-------------+-------------+-------------+-------+
| CheckId | jobId | payRate | jobLocation | hours | date_paid | empId |
+------------+---------------+---------+-------------+-------------+-------------+-------+
| 1512 | entertainment | 12 | store1 | 10.00 | 2013-03-02 | 1 |
| 1510 | entertainment | 12 | store1 | 8.00 | 2013-03-01 | 1 |
| 1507 | retail | 10 | store1 | 8.00 | 2013-03-18 | 1 |
| 1506 | retail | 10 | store1 | 20.00 | 2013-03-19 | 1 |
+------------+---------------+---------+-------------+-------------+-------------+-------+
What i want is to calculate the sum of all hours for all specific jobId , in this case if i did the
query correctly it would look like this:
+---------------+---------------+---------+
| payID | payRate | hours |
+---------------+---------------+---------+
| entertainment| 12 | 18 |
| retail | 10 | 28 |
+---------------+---------------+---------+
In this case there is only two jobIds but it could have more than 2
This is the query i have and its only showing one payId, so I need help fixing it
also note that email is an attribute of my users table
<table>";
$query = "SELECT jobId, payRate, SUM(hours) AS 'All_Hours'
FROM users INNER JOIN deposit ON userId = empId
WHERE users.email = '" . $_SESSION['email'] ."'
GROUP BY jobId,payRate";
if (!$result) { //if the query failed
echo("Error, the query could not be executed: " .
mysqli_error($db) . "</p>");
mysqli_close($db); //close the database
} //by now we have made a successful query
while ($row = mysqli_fetch_assoc($result)){
echo "<tr><td>" .$row['jobId'] . "</td>
<td>" .$row['payRate'] . "</td>
<td>" .$row['All_Hours'] . "</td>
</tr>";
}
echo"</table>
you forgot to add GROUP BY clause in your query causing to have only one record in the result,
SELECT jobId, payRate, SUM(hoursWorked) AS 'All_Hours'
FROM users INNER JOIN paycheck ON userId = empId
WHERE users.email = 'session_email_here'
GROUP BY jobId, payRate
On the deposit table
SELECT jobId, payRate, sum(hours) FROM deposit
group by 1, 2
I would also suggest that you make it little bit more relational: jobId and jobLocation needs to be moved to new tables

Categories