Removing redundant SQL queries (in PHP) by using JOIN - php

I am trying to clean up my SQL queries and use JOIN in just ONE where I once used TWO queries.
Here is former code (in PHP):
$cat = "books"; // as a test
$query = "SELECT category, cat_id FROM master_cat WHERE category = '{$cat}'";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
$cat_id = $row['cat_id'];
}
$sql = mysql_query("SELECT item, spam, cat_id FROM items WHERE cat_id = ' " . $cat_id . "' ORDER BY TRIM(LEADING 'The ' FROM item) ASC;");
while ($row = mysql_fetch_assoc($sql))
if ($row['spam'] < 2)
$output[] = $row;
print(json_encode($output)); // added!
I am trying to just remove the top query and use a JOIN. The updated SQL statement is this:
EDIT: I made a mistake in original question. Basically user input gives us $cat = "something". There is "something" in master_cat table with a cat_id. That cat_id is also in the items table. That is where I need the tables to connect -- and the WHERE clause needs to incorporate "$cat"
UPDATED QUERY:
$result = mysql_query("SELECT i.item, i.spam, mc.cat_id AS Category
FROM items as i
INNER JOIN master_cat as mc
ON i.cat_id = mc.cat_id
WHERE i.cat_id = '{$cat}'
ORDER BY TRIM(LEADING 'The ' FROM i.item) ASC;");
then:
while ($row = mysql_fetch_assoc($result))
if ($row['spam'] < 2)
$output[] = $row;
I receive this in the browser:
null.
Can someone guide me on how to properly use JOIN which I know will REALLY clean things up here and make more efficient coding. I just watched a tutorials but still am not quite getting it.
HERE IS FINAL CODE THAT WORKS
$cat = $_POST['category']; // yes, yes, injection. this is just the short version.
$result = mysql_query("SELECT i.item, i.cat_id, i.spam, mc.cat_id, mc.category, TRIM(LEADING 'The ' FROM i.item) as CleanItem
FROM items as i
INNER JOIN master_cat as mc
ON i.cat_id = mc.cat_id
WHERE mc.category = '{$cat}'
ORDER BY CleanItem ASC;");
while ($row = mysql_fetch_assoc($result))
if ($row['spam'] < 2)
$output[] = $row;

You have reference the items table by its full name in the WHERE clause, you should be using the alias you created (i).
You also have an ambiguous column reference item in your ORDER BY clause.
Try changing the last two lines to:
WHERE i.cat_id = '{$cat_id}'
ORDER BY TRIM(LEADING 'The ' FROM i.item) ASC
You should also inspect mysql_error() to get a string description of the error, which would have pointed you straight to the problem.

$sql = "SELECT i.item, i.spam, mc.cat_id AS Category
FROM items as i
INNER JOIN master_cat as mc
ON i.cat_id = mc.category
WHERE items.cat_id = '{$cat_id}'
ORDER BY TRIM(LEADING 'The ' FROM item) ASC";
Since you aliased it you have to keep it as aliased: fix the second to last line:
WHERE i.cat_id = '{$cat_id}'

Try this one:
$sql = "SELECT i.item, i.spam, mc.cat_id AS Category
FROM items as i
INNER JOIN master_cat as mc ON i.cat_id = mc.cat_id
WHERE i.cat_id = '{$cat_id}'
ORDER BY TRIM(LEADING 'The ' FROM item) ASC";
If you alias items as i, you should use i everywhere else. Besides, mc.category does not seem to exist so I replaced it with mc.cat_id. What does mysql_error say?

You are probably getting false as your response to the initial query and not a result set
You should join on i.cat_id = mc.cat_id
You should also probably perform your i.item cleaning (i.e. removing 'The ') in the select statement (even as a seperate field if you need to keep i.item intact) and then order by that field.
You should reference i.cat_id = '{$cat_id]}', not items.cat_id

Related

Getting multiple rows of the same thing from database

I'm having some issues with this mysql database i got from school. I'm getting duplicate rows with the same name but different quantities. I would suppose that this is because 'inventory' contains columns with the same name as in 'sets'. But i really can't get it to work. Thank you in advance for all your help.
Database schematic (in Swedish, but the titles are in English.):
http://weber.itn.liu.se/~stegu76/TNMK30-2016/legodatabasen.pdf
I'm very new to this so any tips would be appreciated.
$setidquery = "SELECT inventory.Quantity, inventory.ItemID, inventory.ColorID, colors.Colorname, parts.Partname, sets.SetID, sets.Year, inventory.ItemtypeID FROM `inventory`, `parts`, `colors`, `sets` WHERE sets.SetID='$_COOKIE[setid]' AND inventory.ItemID=parts.PartID AND inventory.ColorID=colors.ColorID ORDER BY Partname ASC LIMIT 1000";
echo "<table class=\"table\">";
echo "<tr><th>Quantity:</th><th>Year:</th><th>Partname:</th><th>ItemID:</th><th>SetID</th><th>Image:</th></tr>";
while($row = mysqli_fetch_array($result)){
$prefix = "http://www.itn.liu.se/~stegu76/img.bricklink.com/";
$Year = $row['Year'];
$Quantity = $row['Quantity'];
$ItemID = $row['ItemID'];
$ColorID = $row['ColorID'];
$Partname = $row['Partname'];
$SetID = $row['SetID'];
$ItemtypeID = $row['ItemtypeID'];
$imagesearch = mysqli_query($conn, "SELECT * FROM `images` WHERE ItemTypeID = '$ItemtypeID' AND ItemID = '$ItemID' AND ColorID = '$ColorID' ");
$imageinfo = mysqli_fetch_array($imagesearch);
if($imageinfo['has_jpg']) {
$filename = "$ItemtypeID/$ColorID/$ItemID.jpg";
} else if($imageinfo['has_gif']) {
$filename = "$ItemtypeID/$ColorID/$ItemID.gif";
} else {
$filename = "noimage_small.png";
}
echo "<tr>
<td>$Year</td>
<td>$Quantity</td>
<td>$Partname</td>
<td>$ItemID</td>
<td>$SetID</td>
<td><img src=\"$prefix$filename\" alt=\"Part $ItemID\"/></td>
</tr>";
}
echo "</table>";
Here is the query formatted for readability:
SELECT inventory.Quantity,
inventory.ItemID,
inventory.ColorID,
colors.Colorname,
parts.Partname,
sets.SetID, sets.Year,
inventory.ItemtypeID
FROM `inventory`, `parts`, `colors`, `sets`
WHERE sets.SetID='$_COOKIE[setid]'
AND inventory.ItemID=parts.PartID
AND inventory.ColorID=colors.ColorID
ORDER BY Partname ASC LIMIT 1000;
The problem is that inventory, parts and colors are not joined to sets.
It is OK to do the joins in the where clause, but, as Strawberry says we prefer to write the joins in the from clause now. I think that makes them easier to read, which helps to spot missing joins.
Here is your select statement (formatted to help me see what is going on):
select
inventory.quantity,
inventory.itemid,
inventory.colorid,
colors.colorname,
parts.partname,
sets.setid,
sets.year,
inventory.itemtypeid
from
inventory,
parts,
colors,
sets
where
sets.setid='$_cookie[setid]' and
inventory.itemid = parts.partid and
inventory.colorid = colors.colorid
order by
partname asc
limit 1000;
And here is the join version. I've shown the missing join with "?" where you need to add something:
select
inventory.quantity,
inventory.itemid,
inventory.colorid,
colors.colorname,
parts.partname,
sets.setid,
sets.year,
inventory.itemtypeid
from
inventory
join
parts
on inventory.itemid = parts.partid
join
colors,
on inventory.colorid = colors.colorid
join
sets
on set.? = ?.?
where
sets.setid='$_cookie[setid]'
order by
partname asc
limit 1000;
The important thing is that there is no join to sets. I would guess that there are three sets (for each of the examples shown anyway). Because you have not said how to join to sets the query returns the other rows once for each set, in other words it joins to every set because you haven't restricted the join in any way.
If you want to learn more about SQL there are many options. My website is www.thedatastudio.net.

PHP search box issuse

I have a problem with some php code. So, when I write some text inside search box I should get more results, but I only get 1. This happened to my when I added second query with INNER JOIN. I have no idea why I'm getting only 1 result instead of more, anyone can help?
When I remove second query, it shows me all results.
$STH = $DBH->prepare('SELECT * FROM tv_shows WHERE title like :q ORDER BY title ASC LIMIT 5');
$STH->setFetchMode(PDO::FETCH_OBJ);
$STH->execute(array(
':q' => "%$q%"
));
if($STH->rowCount()) {
while($row = $STH->fetch()) {
$poster = $row->poster;
$mtitle = $row->title;
$mrd = $row->release_date;
$mid = $row->id;
$genres = "";
$STH = $DBH->prepare('SELECT g.title from genres g INNER JOIN tv_show_genres tg ON g.id = tg.genre_id INNER JOIN tv_shows t ON t.id = tg.tv_show_id WHERE t.id = :tid');
$STH->setFetchMode(PDO::FETCH_OBJ);
$STH->execute(array(
':tid' => $mid
));
if($STH->rowCount()) {
while($row = $STH->fetch()) {
$genres .= $row->title.", ";
}
echo
'<li>
<span class="search-poster"><img src="'.$poster.'"></span>
<span class="search-title">'.$mtitle.' ('.$mrd.')</span>
<span class="search-genre">'.substr($genres,0,-2).'</span>
</li>';
}
}
}
You're using the same variable $STH for both queries. So when the outer loop gets back to the
while ($row = $STH->fetch())
line, $STH now refers to the second query. Since you've reached the end of the results from that query, calling fetch() here returns false, so this loop ends as well.
Just use different variable names, e.g. $show_STH and $genre_STH.
However, an even better solution is to use a single query.
SELECT s.poster, s.title AS show_title, s.release_date, g.title AS genre_title
FROM (SELECT *
FROM tv_shows
WHERE title like :q
ORDER BY title ASC
LIMIT 5) AS s
INNER JOIN tv_show_genres tg ON s.id = tg.tv_show_id
INNER JOIN genres g ON tg.genre_id = g.id
ORDER BY s.title
Most of the time when you find yourself performing queries in nested loops like this, you can replace it with a single query that joins the two queries.

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

add value to result from mysql query that will be JSON encoded in PHP?

I would like to add a value to each row that I get from my query depending on if a row exist in another table. Is there a smart way to achieve this?
This is the code I have:
$sth = mysql_query("SELECT tbl_subApp2Tag.*, tbl_tag.* FROM tbl_subApp2Tag LEFT JOIN tbl_tag ON tbl_subApp2Tag.tag_id = tbl_tag.id WHERE tbl_subApp2Tag.subApp_id = '".$sub."' ORDER BY tbl_tag.name ASC");
if(!$sth) echo "Error in query: ".mysql_error();
while($r = mysql_fetch_assoc($sth)) {
$query = "SELECT * FROM tbl_userDevice2Tag WHERE tag_id='".$r['id']."' AND userDevice_id='".$user."'";
$result = mysql_query($query) or die(mysql_error());
if (mysql_num_rows($result)) {
$r['relation'] = true;
$rows[] = $r; //Add 'relation' => true to this row
} else {
$r['relation'] = false;
$rows[] = $r; //Add 'relation' => false to this row
}
}
print json_encode($rows);
Where the //Add ... is, is where I would like to insert the extra value. Any suggestions of how I can do this?
I'm still a beginner in PHP so if there are anything else that I have missed please tell me.
EDIT: Second query was from the wrong table. This is the correct one.
Edited Edited below query to reflect new information because I don't like leaving things half-done.
$sth = mysql_query("
SELECT
tbl_subApp2Tag.*,
tbl_tag.*,
ISNULL(tbl_userDevice2Tag.userDevice_id) AS relation
FROM tbl_subApp2Tag
LEFT JOIN tbl_tag
ON tbl_tag.id = tbl_subApp2Tag.tag_id
LEFT JOIN tbl_userDevice2Tag
ON tbl_userDevice2Tag.tag_id = tbl_tag.id
AND tbl_userDevice2Tag.userDevice_id = '".$user."'
WHERE tbl_subApp2Tag.subApp_id = '".$sub."'
ORDER BY tbl_tag.name ASC
");
Though the above feels like the LEFT JOIN on tbl_tag is the wrong way around, but it's hard to tell as you are vague on your eventual aim. For example, if I was to assume the following
Tags will always exist
subApp2Tag will always exist
You want to know if a record in tbl_userDevice2Tag matches the above
Then I would do the following instead. The INNER JOIN means that it won't worry about records in tbl_tag that are not on the requested subApp_id which in turn will limit the other joins.
$sth = mysql_query("
SELECT
tbl_subApp2Tag.*,
tbl_tag.*,
ISNULL(tbl_userDevice2Tag.userDevice_id) AS relation
FROM tbl_tag
INNER JOIN tbl_subApp2Tag
ON tbl_subApp2Tag.tag_id = tbl_tag.id
AND tbl_subApp2Tag.subApp_id = '".$sub."'
LEFT JOIN tbl_userDevice2Tag
ON tbl_userDevice2Tag.tag_id = tbl_tag.id
AND tbl_userDevice2Tag.userDevice_id = '".$user."'
ORDER BY tbl_tag.name ASC
");
you have to do all the job in a single query.
Why can't you just $r['append'] = "value"; before adding $r to the array?

Not a unique table/alias stock:

Ive got the query below :
$sql = "SELECT `scanners`.`KordNo`, `scanners`.`BundleNumber`
FROM `scanners`, `TWOrder`, `Stock`
INNER JOIN `TWORDER` ON `scanners`.`KordNo` = `TWOrder`.`KOrdNo`
AND `scanners`.`Date` = '" . $date . "'
INNER JOIN `Stock` ON `TWOrder`.`Product` =`Stock`.`ProductCode`
AND `Stock`.`ProductGroup` NOT BETWEEN 400 AND 650
AND `scanners`.`Scanner` IN (
ORDER BY `scanners`.`KordNo` ASC";
foreach($scanner as $x)
{$sql .= $x . ",";}
$sql .= "0);";
// And query the database
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
$return[] = $row;
}
When i echo the sql on php my admin i get the error not a unique table/alias stock;
can someone advise?
Since you're using explicit JOINs, drop the other two tables off of the FROM clause.
...
FROM `scanners`
INNER JOIN `TWORDER` ON `scanners`.`KordNo` = `TWOrder`.`KOrdNo`
...
On line 2 you have...
FROM `scanners`, `TWOrder`, `Stock`
Then you have some INNER JOINs on to TWOrder and Stock.
That's mixing syntax (, and JOIN) which is messy. Stick to JOIN
It means that TWOrder and Stock are mentioned Twice in the query
If you REALLY need to include those table multiple times in one query, you need to give them alias names, so they can be distiguished from each other.
But I think it's probably a mistake and that Line 2 should just be
FROM `scanners`
Then, also, I'm not sure how you got that to compile. You have IN ( and then an ORDER BY clause, to which you append a list of values. You should append the list before the ORDER BY and then append the ORDER BY after you've finished the loop.

Categories