How to optimize nested query with PHP and MySqli? - php

I have this code, that fetches data from two tables in MySQLi.
It first has to get the title, description, status and project_id from table 1, and then get the name from table 2 using the id from table 1.
Is there a better/faster way to do this? I have about 600 rows in the tables, and it takes about 5 sec to run this query. I will also add that this example is a bit simplified, so please don't comment on the db-structure.
<?php
$results = $connect()->db_connection->query(
'SELECT title, description, status, project_id
FROM table
WHERE created_by ='.$user_id
);
if ($results) {
while ($result = $results->fetch_object()) {
$res = $connect()->db_connection->query(
"SELECT name FROM projects WHERE id = ".$result->project_id
);
if ($res) {
while ($r = $res->fetch_object()) {
echo $r->name;
}
}
echo $result->title;
echo $result->status;
}
}
?>

Use Query:
SELECT title,description,status,project_id
FROM table tb
inner join projects pr on pr.id = tb.project_id
WHERE created_by = $user_id

Try to use JOIN in your query.
You can find examples and description of this command here: http://www.w3schools.com/sql/sql_join.asp
Check out also this infographics:
http://www.codeproject.com/KB/database/Visual_SQL_Joins/Visual_SQL_JOINS_orig.jpg

You can use JOIN on project_id:
$results = $connect()->db_connection->query('SELECT t.title title,t.description,t.status status,t.project_id, p.name name FROM `table` t JOIN projects p ON p.id= t.project_id WHERE t.created_by ='.$user_id);
if($results){
while($result = $results->fetch_object()){
echo $result->name;
echo $result->title;
echo $result->status;
}
}
tables have aliases here - t for table and p for projects.
Also to make it faster, add index to project_id in table table, if you haven't done it yet:
$connect()->db_connection->query('ALTER TABLE `table` ADD INDEX `product_id`');

Related

Echo contents of JOIN SQL tables with MySQLi

I'm working on a system, and this module is supposed to echo the contents of the database.
It worked perfectly until I added some JOIN statements to it.
I've checked and tested the SQL code, and it works perfectly. What's not working is that part where I echo the content of the JOINed table.
My code looks like this:
$query = "SELECT reg_students.*, courses.*
FROM reg_students
JOIN courses ON reg_students.course_id = courses.course_id
WHERE reg_students.user_id = '".$user_id."'";
$result = mysqli_query($conn, $query);
if (mysqli_fetch_array($result) > 0) {
while ($row = mysqli_fetch_array($result)) {
echo $row["course_name"];
echo $row["course_id"];
The course_name and course_id neither echo nor give any error messages.
UPDATE: I actually need to increase the query complexity by JOINing more tables and changing the selected columns. I need to JOIN these tables:
tutors which has columns: tutor_id, t_fname, t_othernames, email, phone number
faculty which has columns: faculty_id, faculty_name, faculty_code
courses which has columns: course_id, course_code, course_name, tutor_id, faculty_id
I want to JOIN these tables to the reg_students table in my original query so that I can filter by $user_id and I want to display: course_name, t_fname, t_othernames, email, faculty_name
I can't imagine that the user_info table is of any benefit to JOIN in, so I'm removing it as a reasonable guess. I am also assuming that your desired columns are all coming from the courses table, so I am nominating the table name with the column names in the SELECT.
For reader clarity, I like to use INNER JOIN instead of JOIN. (they are the same beast)
Casting $user_id as an integer is just a best practices that I am throwing in, just in case that variable is being fed by user-supplied/untrusted input.
You count the number of rows in the result set with mysqli_num_rows().
If you only want to access the result set data using the associative keys, generate a result set with mysqli_fetch_assoc().
When writing a query with JOINs it is often helpful to declare aliases for each table. This largely reduces code bloat and reader-strain.
Untested Code:
$query = "SELECT c.course_name, t.t_fname, t.t_othernames, t.email, f.faculty_name
FROM reg_students r
INNER JOIN courses c ON r.course_id = c.course_id
INNER JOIN faculty f ON c.faculty_id = f.faculty_id
INNER JOIN tutors t ON c.tutor_id = t.tutor_id
WHERE r.user_id = " . (int)$user_id;
if (!$result = mysqli_query($conn, $query)) {
echo "Syntax Error";
} elseif (!mysqli_num_rows($result)) {
echo "No Qualifying Rows";
} else {
while ($row = mysqli_fetch_assoc($result)) {
echo "{$row["course_name"]}<br>";
echo "{$row["t_fname"]}<br>";
echo "{$row["t_othernames"]}<br>";
echo "{$row["email"]}<br>";
echo "{$row["faculty_name"]}<br><br>";
}
}

Generate html table based on 2x mysql db queries

I'm trying to show stuff queried from two tables, but on one html table. Data is shown for the last 30 days, based on which, an html table is being generated.
Currently I'm stuck using two queries and generating two html tables:
$query1 = mysqli_query( $con, "SELECT date, stuff* " );
while( $record = mysqli_fetch_array( $query1 ) ){
echo '<html table generated based on query>';
}
$query2 = mysqli_query( $con, "SELECT date, other stuff*" );
while( $record = mysqli_fetch_array( $query2 ) ){
echo '<another html table generated based on query2>';
}
Is there a possibility to show both queries on one html table instead?
Note that it gets tricky since we have dates on one table which are not necessarily found in the second table or vice-versa.
Thanks for the support guys. So far I'm stuck at this:
SELECT * FROM user_visit_logs
LEFT JOIN surfer_stats ON user_visit_logs.date = surfer_stats.date
UNION
SELECT * FROM user_visit_logs
RIGHT JOIN surfer_stats ON user_visit_logs.date = surfer_stats.date
The query completes, but the 2nd table fields are all null:
Furthermore, it breaks when I add additional clause like:
WHERE user_id = '{$_SESSION['user_id']}' ORDER BY date DESC LIMIT 30
I think you are after FULL OUTER JOIN concept:
The FULL OUTER JOIN keyword returns all rows from the left table (table1) and from the right table (table2)
In which you may use common dates as a shared row.
So the query will get to simple one:
$query = "
SELECT table1.date, stuff
FROM table1
LEFT OUTER JOIN table2 ON table1.date = table2.date
UNION
SELECT table2.date, other_stuff
FROM table1
RIGHT OUTER JOIN table2
ON table1.date = table2.date
";
$result = mysqli_query( $con, $query );
while( $record = mysqli_fetch_array( $result ) ){
echo '<html table generated based on query>';
}
Example
This is an schematic diagram of FULL OUTER JOIN concept:
After running into quite a few bumps with this one, I finally managed to merge 2 columns from each table and also to use where and sort clauses on them with the following query:
( SELECT user_visit_logs.user_id,user_visit_logs.date,unique_hits,non_unique_hits,earned,sites_surfed,earnings FROM user_visit_logs
LEFT OUTER JOIN surfer_stats ON user_visit_logs.user_id = surfer_stats.user_id AND user_visit_logs.date = surfer_stats.date where user_visit_logs.user_id = 23 ORDER BY date DESC LIMIT 30 )
UNION
( SELECT surfer_stats.user_id,surfer_stats.date,unique_hits,non_unique_hits,earned,sites_surfed,earnings FROM user_visit_logs
RIGHT OUTER JOIN surfer_stats ON user_visit_logs.user_id = surfer_stats.user_id AND user_visit_logs.date = surfer_stats.date where user_visit_logs.user_id = 23 LIMIT 30 )
Simplified, "user_visit_logs" and "surfer_stats" were the 2 tables needed to be joined.
Absolutely. Just pop them both into a variable:
$data = '';
$query = mysqli_query($con,"SELECT date, stuff* ");
while($record = mysqli_fetch_array($query)) {
$data.= '<tr><td>--Your Row Data Here--</td></tr>';
}
$query2 = mysqli_query($con,"SELECT date, other stuff*");
while($record = mysqli_fetch_array($query2)) {
$data .= '<tr><td>--Your Row Data Here--</td></tr>';
}
echo "<table>$data</table>";
Instead of using echo in your loop, you're just storing the results in $data. Then, you're echoing it out after all data has been added to it.
As for your second point, it's not a big deal if fields don't exist. If they're null, you'll just have a column that doesn't have data in it.
Here's an example with fake column names:
$data = '';
$query = mysqli_query($con,"SELECT date, stuff* ");
while($record = mysqli_fetch_array($query)) {
$data.= "<tr><td>{$record[id]}</td><td>{$record[first_name]}</td><td>{$record[last_name]}</td></tr>";
}
$query2 = mysqli_query($con,"SELECT date, other stuff*");
while($record = mysqli_fetch_array($query2)) {
$data .= "<tr><td>{$record[id]}</td><td>{$record[first_name]}</td><td>{$record[last_name]}</td></tr>";
}
echo "<table><tr><th>ID</th><th>First Name</th><th>Last Name</th></tr>$data</table>";
I have a feeling I may have misunderstood the need. If so, I apologize. If you can elaborate just a bit more I can change my answer :)

Get data from a join table

I am trying to pull data from my database which ahs the following:
x3 tables:
docs
doc_id - index
cat
cat_id - index
doc_cat_join
doc_id - foreign key to docs/doc_id
cat_id - foreign key to cats/cat_id
I have inserted some data so that I have a row in cats and a rows in docs and in the join table bound a cat_id with a doc_id which all works fine. I am tying to pull that from the tables and show it, here is my approach so far but not getting anything out and am wondering where my failings are as I am getting no errors?
when I visit the cateroy page in my scripts I GET the id from the url:
$id = $_GET['cat_id'];
$q = sprintf("
SELECT
docs.doc_id,doc_name
FROM docs
INNER JOIN doc_cat_join
ON cats.cat_id = doc_cat_join.cat_id
WHERE doc_cat_join.doc_id = '%s'
",
mysqli_real_escape_string($dbc, $id) );
$r = mysqli_query($dbc,$q)
or die ("Couldn't execute query: ".mysqli_error($dbc));
// FETCH AND PRINT ALL THE RECORDS
echo '<div class="view_body">';
while ($row = mysqli_fetch_array($r)) {
echo ' '.$row["doc_name"]. '<br>';
}
echo '</div>';
}
for some bizarre reason I get nothing, if I do a var_dump on $r I get the following:
string(168) " SELECT docs.doc_id,doc_name FROM docs INNER JOIN doc_cat_join ON cats.cat_id = doc_cat_join.cat_id WHERE doc_cat_join.doc_id = '24' "
So it gets the correct category ID I am in.
I am now getting th following output:
Couldn't execute query: Unknown column 'cats.cat_id' in 'on clause'
as per your input and query design, your query should be as per below-
SELECT d.doc_id, d.doc_name
FROM docs AS d
INNER JOIN doc_cat_join AS dc ON dc.doc_id = d.doc_id WHERE dc.cat_id = '24';
Note: If it is not then show what output you require.
$id = $_GET['cat_id'];
$q = sprintf("
SELECT
*
FROM docs
INNER JOIN doc_cat_join
ON docs.doc_id = doc_cat_join.doc_id
INNER JOIN cat
ON doc_cat_join.cat_id = cat.cat_id
WHERE doc_cat_join.cat_id = '$id'
",
mysqli_real_escape_string($dbc, $id) );
print_r ($q);
// FETCH AND PRINT ALL THE RECORDS
echo '<div class="view_body">';
while ($row = mysqli_fetch_array($r)) {
echo ' '.$row["doc_name"]. '<br>';
}
echo '</div>';
}
your query is wrong I think.

Why does one of my column shows empty?

I have my table that one of my column shows empty. It has the column of Id, Date, Cust name, Product + Qty, and amount. But only in Product + Qty shows empty even it has data in database.
PHP code
<?php
include('connect.php');
$start = isset($_GET['d1']) ? $_GET['d1'] : '';
$end = isset($_GET['d2']) ? $_GET['d2'] : '';
if(isset($_GET['submit']) && $_GET['submit']=='Search')
{
$result = mysql_query(
"SELECT
t1.qty,
t2.lastname,
t2.firstname,
t2.date,
t3.name,
t2.reservation_id,
t2.payable FROM prodinventory AS t1
INNER JOIN reservation AS t2
ON t1.confirmation=t2.confirmation
INNER JOIN products AS t3
ON t1.room=t3.id
WHERE str_to_date(t2.date, '%d/%m/%Y') BETWEEN
str_to_date('$start', '%d/%m/%Y') AND
str_to_date('$end', '%d/%m/%Y')
GROUP BY t2.confirmation") or die(mysql_error());
while ($row = mysql_fetch_array($result)){
echo'<tr class="record">';
echo '<td>'.$row['reservation_id'].'</td>';
echo '<td>'.$row['date'].'</td>';
echo '<td>'.$row['firstname'].' '.$row['lastname'].'</td>';
echo '<td><div align="left">';
$rrr=$row['confirmation'];
$results = mysql_query("SELECT * FROM prodinventory where confirmation='$rrr'");
while($row1 = mysql_fetch_array($results))
{
$roomid=$row1['room'];
$resulta = mysql_query("SELECT * FROM products where id='$roomid'");
while($rowa = mysql_fetch_array($resulta))
{
echo $rowa['name'].' x';
}
echo ' '.$row1['qty'].'<br>';
}
echo '<td>'.'PHP ' . number_format(floatval($row['payable']));
}
?>
Hmmmm I have deleted my answer but noone tried so...
I think this echo ' '.$row1['qty'].'<br>'; is the row you asked about. And all this looks like a typo. If this is the case:
You have no confirmation in the SELECT clause (it's used only in JOIN and GROUP BY) and it possible your $rrr to be blank. Echo it to be sure there is a value.
Check does your query works and return results. Echo the query string (or take it from the mysql log file) and test it.
You have SELECT *. Is the field name 'qty' correct in a case-sensivity environment? 'Qty' may be different and the query may work but you don't get the result.
i think that's because you have inner join and maybe the intersection tables have no data
try to do left join first if it works
insure that all of tables have data in it

php foreach in foreach in foreach

This is a hypothetical question. If I have 3 arrays from 3 separate sql db queries that all relate to another. For example...
//db
schools
id | school_name
classes
id | class_name | school_id
students
id | student_name | class_id
And I want to display everything in a huge list like this...
//php
foreach(schools as school){
echo '<h1>' . $school->school_name . '<h1>';
foreach(classes as class){
if($class->school_id == $school->id){
echo '<h2>' . $class->class_name . '<h2>';
foreach(students as student){
if($student->class_id == $class->id){
echo '<h3>' . $student->student_name . '<h3>';
}
}
}
}
}
I have to make 3 database calls. Is there a way to grab all this information in a single db query? Like maybe an array in an array in an array and then somehow loop through? Or is this the best way to do this?
You can do a join which will allow you to have 1 for each. Are you wanting everything or any sort of filter ?
You can join those table, to get one big array with flattened data. When looping through this data, you can check if the id of the previous record still matches the id of the current record. If not, you can output a new header. It is important, though, that the resultset is properly sorted for this.
SELECT
s.id AS school_id,
s.school_name,
c.id AS class_id,
c.class_name,
st.id AS student_id,
st.student_name
FROM
schools s
INNER JOIN classes c ON c.school_id = s.id
INNER JOIN students st ON st.class_id = c.id
ORDER BY
s.id,
c.id,
st.id
If you have it all in a flattened structure, you can even make it into a nested array structure again something like this:
foreach ($resultset as $row)
{
$schools[$row->school_id]->school_name =
$row->school_name;
$schools[$row->school_id]->classes[$row->class_id]->class_name =
$row->class_name;
$schools[$row->school_id]->classes[$row->class_id]->students[$row->student_id]->student_name =
$row->student_name;
}
var_dump($schools);
After that, you can still use the nested for loops to process the array, but it will be in a more efficient way, since the data is already sorted out: classes are already added to the school they belong to, and student are already added to the right class.
<?php
try {
$pdo = new PDO("mysql:host=127.0.0.1;dbname=school", "username");
} catch (PDOException $e) {
echo "PDO Connection failed: " . $e->getMessage();
exit(1);
}
$sql = <<<SQL
SELECT schools.school_name, classes.class_name, students.student_name
FROM
schools INNER JOIN classes ON (schools.id = classes.school_id)
INNER JOIN students ON (classes.id = students.class_id)
ORDER BY 1, 2;
SQL;
$result = $pdo->query($sql);
if ($result == false) {
die("query failed?!");
}
$school = "";
$class = "";
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
if ($school != $row['school_name']) {
$school = $row['school_name'];
echo "\nSchool: $school\n\n";
}
if ($class != $row['class_name']) {
$class = $row['class_name'];
echo " Class: $class\n\n";
echo " Student list:\n";
}
echo " {$row['student_name']}\n";
}
$res = mysql_query('SELECT school_name, class_name, student_name, sc.id AS scid, c.id AS cid, st.id AS stid FROM schools sc LEFT JOIN classes c ON (sc.id = c.school_id) LEFT JOIN students st ON (c.id = st.class_id) ');
$arr = array();
while ($v = mysql_fetch_assoc($res)) {
$arr[$v['school_name']][$v['class_name']][$v['stid']] = $v['student_name'];
}
print_r($arr);
You could do it all in one SQL query that might look something like:
SELECT schools.schoolname, classes.class_name, students.student_name
FROM
schools INNER JOIN classes ON (schools.id = classes.school_id)
INNER JOIN students ON (classes.id = students.class_id)
ORDER BY 1, 2;
Then you could walk the result set in one loop, but you'd probably want to add some logic to only display the school name and class name once for each time it changes.

Categories