How can I combine 3 tables in a INNER JOIN?
The end result I am after is getting a list of CATEGORIES belonging to a PRODUCT - including the CATEGORY'S PARENT ID CATEGORY value (ie: Sneakers and Nike).
The CATEGORIES table and PRODUCTS table are joined in the PRODUCTS & CATEGORIES table. A product can belong to many categories and a category can have many products.
Here's more-or-less the setup I have in my database...
CATEGORIES TABLE:
CAT ID | PARENT ID | CATEGORY
1 | 0 | Sneakers
2 | 1 | Nike
3 | 2 | Jordan
PRODUCTS TABLE:
PROD ID
1
2
3
PRODUCTS & CATEGORIES TABLE:
CAT ID | PROD ID
1 | 0
1 | 1
2 | 3
I am running these queries and I am getting some results, but at the moment I am running 2 separate queries...
$q1 = "SELECT prodid, GROUP_CONCAT(catid SEPARATOR ' // ') as catid FROM products_categories group by prodid order by prodid";
$result1 = $conn->query($q1);
if ($result1->num_rows > 0) {
while($prods = $result1->fetch_assoc()) {
echo "Product Id:" . $prods["prodid"] . " ––> " . "Categories Id:" . $prods["catid"];
}
} else {
echo "0 results";
}
$q2 =
" SELECT `ID`.`category` as `IDName`, `LABEL`.`category` as `LabelName`, `LABEL`.`catid` as `LabelId`
FROM `categories` as ID
INNER JOIN `categories` as LABEL
ON `ID`.`catid` = `LABEL`.`parentid`";
$result2 = $conn->query($q2);
if ($result2->num_rows > 0) {
while($prods = $result2->fetch_assoc()) {
echo "ID# " . $prods["LabelId"] . " is called: ". $prods["LabelName"] . "<br>";
}
} else {
echo "0 results";
}
$conn->close();
I have tried adding another INNER JOIN with no luck in the results.
The end result I am after would be: PROD ID #0 belongs to Sneakers, Nike, Jordan.
Anyone can point me in the right direction?
Thank you so much,
Sergio
UPDATE - 10/11/16
The Query:
$q =
" SELECT PC.productid as productid, concat_WS('~',C1.category, C2.category, C3.category) as breadcrumb
FROM xcart_categories as C1
INNER JOIN xcart_products_categories as PC
ON C1.categoryid = PC.categoryid
LEFT JOIN xcart_categories as C2
ON C1.categoryid = C2.parentid
AND C1.parentid = 0
LEFT JOIN xcart_categories as C3
ON C2.categoryid = C3.parentid
WHERE C1.parentid = 0
";
The Fetch:
$result = $conn->query($q);
if ($result->num_rows > 0) {
while($prods = $result->fetch_assoc()) {
echo $prods['productid'] . ' Belongs in these categories: ' . $prods['breadcrumb'] . '<br>';
}
} else {
echo "0 results";
}
This assumes 3 levels of hierarchy no more and a separate join is needed to "put each record on the same line" so they can be combined into a single value result. I thin you were trying to use Group_concat but I can't see how that's going to work as you don't have a way to walk the hierarchy.
SELECT PC.ProductID, concat_WS('-',C1.Category, C2.Category, C3.Category) as breadcrumb
FROM categories C1
INNER JOIN ProductsCategories PC
on C1.categoryID = PC.CategoryID
LEFT JOIN categories C2
on c1.CategoryID = C2.ParentID
and C1.parentID = 0
LEFT Join Categories C3
on C2.CategoryID = C3.ParentID
WHERE C1.ParentID = 0
Working SQL Fiddle example ( this only supports 3 levels deep, but could be altered with added left joins to support a max level but not a undetermined max level..)
I see you're trying to use group concat to bring all the rows for the same product category.productID of 0 to the same line
However as 0 references catID of 1 it would only return "sneakers" on the inner join. You would need to traverse the tree (all of it) somehow, thus the above, or you have to take multiple trips to the db or use some sort of dynamic SQL or method mentioned in link in comments.
This would be fairly simple in SQL Server, Oracle or other Enterprise RDBMS systems, however without recursive queries or engine specific hierarchy queries, this is no easy feat in MySQL on a single trip.
Maybe I'm missing something so it may help to see the actual expected results for your sample data. What is the record set look like that you want back?
Related
i have 4 database tables (office, facility, course, treatment) the id in office table acts as office id in all other 3 tables.
office
id name address phoneno city
1 O1 address1 12 city1
2 O2 address2 34 city2
3 O3 address2 45 city3
facility
id office_facility office_id
1 F1 1
2 F2 1
3 F3 2
Course
id office_course office_id
1 C1 1
2 C2 2
3 C3 3
Treatment
id office_treatment office_id
1 T1 1
2 T2 2
3 T3 2
i am trying to conduct a search for office on the basis of facility, course and treatment. the search code that i have worked works when there is only one table and the criteria of search is part of that same table, however this case is different.
code for search is
<?php
$con=mysqli_connect("localhost","root","","db");// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$city = mysqli_real_escape_string($con, $_POST['city']);
$sql1 = "SELECT * FROM table WHERE city LIKE '%$city%'";
$result = mysqli_query($con, $sql1);
if (mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_assoc($result)) {
echo "Office name: " . $row["office_name"]. " - Location: " . $row["office_address"]. " " . $row["office_city"]. "<br>";
}
} else {
echo "0 results";
}
mysqli_close($con);
?>
Would appreciate if anyone could tell me how i can search offices from these tables
You might want to perform a complex SQL query against database, concatenating all the conditions you have:
SELECT address FROM office
JOIN facility ON (facility.office_id = office.id)
JOIN course ON (course.office_id = office.id)
JOIN treatment ON (treatment.office_id = office.id)
WHERE city LIKE %...%
AND office_facility LIKE %...%
AND office_course LIKE %...%
AND office_treatment LIKE %...%
The values for LIKE as well as boolean logic on AND/OR is now up to you.
You can use union for that
select A.ID,(SELECT B.Name from office B where A.ID=B.ID) as Name from (
select id from office where <Your Office Table filter Criteria>
UNION
select office_id from facility where <Your facility Table filter Criteria>
UNION
select office_id from Course where <Your Course Table filter Criteria>
UNION
select office_id from Treatment where <Your Treatment Table filter Criteria>
)A
You can use join query. Actually you haven't clarify that what is you need so I ma trying to give you same basic(global) example
"select from table_name as td
left join anoather_table as anoth on td.common_column = anoth.commoncolun whare td.condition_columh= condition"
This question already has an answer here:
Codeigniter - Combining Three tables into correct view format
(1 answer)
Closed 8 years ago.
I have 3 tables - Product, category and product_category.
|------ product-------- |
|-Pid
|-Pname
|------ catrgory ----|
|-cat_id
|-cat_name
|------ product_category --------------|
|-product_id
|-category_id
Here is my attempts.
$comp_query=mysql_query("SELECT DISTINCT p.Pname AS product_name, p.Pid AS product_id,
c.cat_name AS category_name, c.cat_id AS category_id
FROM product AS p
JOIN product_category AS pc ON p.Pid = pc.product_id
JOIN category AS c ON c.cat_id = pc.category_id
Order BY category_id");
while($row=mysql_fetch_array($comp_query)){
echo " $row[category_name] -----";
echo " $row[product_name]<br/> ";
}
this query shows me result as :
Category1 ----- Category1-Product 1
Category1 ----- Category1-Product 2
Category1 ----- Category1-Product 3
Category2 ----- Category2-Product-1
Category2 ----- Category2-Product-2
BUt I want to get product from each category like:
**|-----Category1-----|**
|-Product-1
|-Product-2
|-Product-3
**|-----Category2-----|**
|-Product-1
|-Product-2
|-Product-3
**|-----Category3-----|**
|-Product-1
|-Product-2
|-Product-3
with product name and other details also.
Please help how can I do this with looping and query.
I suggest you to use 2 queries:
$q1 = mysql_query("SELECT * FROM catrgory ORDER BY cat_name");
while($cat = mysql_fetch_assoc($q1)) {
print "**|-----".$cat['cat_name']."-----|**<br>";
$q2 = mysql_query("SELECT * FROM product WHERE Pid IN (SELECT product_id FROM product_category WHERE category_id = ".$cat['cat_id'].") ORDER BY Pname");
while($prod = mysql_fetch_assoc($q2)) {
print " |***".$prod['Pname']."<br>";
}
}
--> Update -->
I gave the 2 queries solutions because i thought you wanted to show empty categories...
in that case, just use your query above and replace your "while" loop with this:
$title_shown ='';
while($row=mysql_fetch_array($comp_query)){
if($row['category_name'] != $title_shown) {
$title_shown = $row['category_name'];
print "\n**|-----".$row['category_name']."-----|**\n";
}
print "|-".$row['product_name']."\n";
}
As for me, your query looks good. You just need to group all products with the same category id at the php level. Just use a map with key=category_id, and value = {arrays of products}.
And populate this map right inside your while loop. Fragment in PSEUDO code just to get the main idea:
while(...) {
if (map.containsKey(category_id) {
map.get(category_id).add(product);
} else {
Array products = new Array();
products.add(product);
map.put(category_id, products)
}
}
I've got two databases fruit and fruit-prices (for arguement's sake).
In fruit there are two columns id | name
Fruit table
id | name
1 | Apple
2 | Banana
In fruit_prices there are three columns id | fruit_id | price where fruit_id is a FOREIGN KEY reference.
id | fruit_id | price
1 | 1 | £2.00
2 | 2 | £3.00
Now I have a PHP function that will print out a table row and cells with the information from the database but currently if I am printing two fruits out my table looks like this.
Name | Price
1 | £2.00
2 | £3.00
PHP Code:
$query = mysqli_query($conn, "SELECT * FROM fruits ");
while($row = mysqli_fetch_assoc($query)) {
$name = $row['fruit_id']; //for comma separation
$price = $row['price'];
echo "<tr>";
echo "<td>" .$name. "</td>" .
"<td>" .$price. "</td>";
echo "</tr>";
}
Is there an elegant way I can retrieve the name of the fruit (i.e. 1 = Apple, 2 = Banana). Rather than using the unique ID of each fruit.
So then my table will look like this
Name | Price
Apple | £2.00
...
I hope this makes sense? I'm new to RD concepts. This is a very simple example and does not reflect my entire project so I'm just wondering if this is achievable?
This can be done by joining both the tables.Change your sql query with below one.I think it will solve your problem.
"SELECT fruit.name,fruit_prices.price FROM fruit,fruit_prices WHERE fruit_prices.fruit_id = fruit.id";
You would want to use a join and specify the columns you would like to select.
SELECT f.name, fp.price FROM fruit as f
JOIN fruit-prices as fp ON f.id=fp.fruit_id;
It is achievable using JOIN:
SELECT * FROM fruits a JOIN fruits-prices b ON b.fruit_id = a.id
For more friendly column names you can add column aliases and use them further.
You say they are in two databases, but I think it might just be two tables. If so:
select * from fruit f
left outer join fruit-prices fp
on f.id = fp.fruit_id
The left outer join ensures that if a fruit doesn't have a price it will be returned will null as the price. If you don't want that replace it with an inner join.
Use of JOIN does what you need.
"SELECT `p`.`price`, `f`.`name` FROM `fruit_prices` `p`
JOIN `fruits` `f` ON `f`.`id` = `p`.`fruit_id`"
Be wary - use of SELECT * here will lead to an error, as both tables have an id field, and the query will break.
You need a join between the tables,
SELECT f.name, fp.price
FROM fruit f
INNER JOIN fruit_price fp ON f.id = fp.fruit_id
Well, you need to take a look to SQL Joins
query = mysqli_query($conn, "SELECT FRUIT.name,FRUIT_PRICES.price FROM fruits INNER JOIN FRUIT_PRICES ON FRUIT.id = FRUIT_PRICES.fruit.id");
while($row = mysqli_fetch_assoc($query)) {
$name = $row['name']; //for comma separation
$price = $row['price'];
echo "<tr>";
echo "<td>" .$name. "</td>" .
"<td>" .$price. "</td>";
echo "</tr>";
}
What I wrote means : "Select FRUIT.Name,FRUIT_PRICES.price in FRUIT AND FRUIT_PRICES, considering that FRUITS.id is related to FRUIT_PRICES.id_fruit"
Suggestion : You can have a single table viz fruits with 3 columns id, fruit_name, price.
for your table structure :
SELECT fruits.id, name, price
FROM fruits, fruit-prices
WHERE fruit_id=fruits.id;
I have 2 tables:
categories (id, categoryName),
menu (id, menuname, category_id)
I would like to display all categories, which have one or more records in the menu.
And after every categoryName to show 5 menuname.
Is it possibe, to do this in the one recordset?
Thank you!
These are my 2 recordsets:
$query = "select a.id, a.name from categories as a where a.id in (select count(*) from menu as b on b.category_id = a.id)";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
echo $row['name'];
$category_id = intval($row['id']);
$query = "select menuname from menu where category_id = $category_id limit 0, 5";
$resultmenu = mysql_query($query);
while ($rowmenu = mysql_fetch_array($resultmenu)) {
echo $rowmenu['menuname'];
}
}
As mentioned above, i'm not sure what is meant by "And after every categoryName to show 5 menuname".
But to show a list of all category/menu names in alphabetical order you could use the following:
SELECT C.categoryName,
M.menuname
FROM categories C
INNER JOIN menu M ON M.category_id = C.id
ORDER BY C.categoryName,
M.menuname
Update:
At the moment your first query will only be returning at best one row. The subquery is currently counting the number of menu rows and then this figure is being used to pull a row from the category table, which isn't what you want.
The following query joins onto the menu table to ensure that at least one item exists, and then groups by the category fields to ensure that each item is only returned once:
$query = "select a.id, a.name from categories as a inner join menu as b on b.category_id = a.id group by a.id, a.name"
Update 2
Ah sorry, I understand now. No I don't think it's possible to achieve what you want in a single query. Even if it were possible I wouldn't recommend it. Looking at your code, you only want to print the categoryName once for each set of menu items. If you were able to pull back the categoryName and menuname items in one result set like so:
| categoryName | menuname |
---------------------------
| category1 | menu1 |
| category1 | menu2 |
| category1 | menu3 |
| category2 | menu4 |
When iterating through the results you would need to manually check when the categoryName had changed in order to print it out once for that set of menuname items.
I`ve a problem with grouping and displaying results from two tables in MySQL db.
I`m trying to make a shopping cart, and I have realised a shop menu which contains products categories and via jquery when category is clicked, menu expands and show products from that category.
Example of categories table:
+------------+
| ID | NAME |
+------------+
| 1 | name1 |
| 2 | name2 |
| 3 | name3 |
| 4 | name4 |
+------------+
Example of products table:
+------------------------------------------------------------------------+
| ID | PR_CODE | NAME | DIMENSIONS | COLORS | OFFER | PRICE | CATEGORY |
+------------------------------------------------------------------------+
| 1 | pr_code1 | prod1 | 40 x 40 | blue | 1 | 11.00 | 1 |
| 2 | pr_code2 | prod2 | 120 x 120 | white | 1 | 12.00 | 1 |
| 3 | pr_code3 | prod3 | 60 x 120 | yellow | 0 | 13.00 | 2 |
| 4 | pr_code4 | prod4 | 40 x 60 | orange | 0 | 14.00 | 3 |
+------------------------------------------------------------------------+
Category row in products table telling which category product belongs.
Code for menu:
<div class="shopMenu">
<ul>
<?php
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";
$query = execute_select($sql);
foreach($query as $row) {
$id = $row['id'];
$name = $row['name'];
$prodId = $row['prodId'];
$prodName = $row['prodName'];
echo '<li>' . $name . '
<ul>
<li>' . $prodName . '</li>
</ul>
</li>'; }
?>
</ul>
</div>
This code works, but works wrong. If I put sql query as:
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category ORDER BY categories.id DESC";
Result is: all categories are listed but categories which contains more products are repeated in main menu as many times as there are products in this category.
If I put sql query as:
$sql = "SELECT categories.*, products.id as prodId, products.name as prodName FROM `categories` LEFT JOIN `products` ON categories.id = products.category GROUP BY categories.id ORDER BY categories.id DESC";
Result is: all categories are listed once, but only one of products from chosen category is listed, missing other products.
Can somebody help with this query?
There is basically two ways of doing what you want, both of them have been explained in the other response and in the comments. I will try to expand a little about them in this answer.
One query
You retrieve all the data in one query and then use PHP to do some transformation in order to show them in the appropriate way.
There's two approaches to do that. You can modify the data first and then loop on them to display the menu or you can do everything in just one loop.
Two steps variant
First, we create an array containing the data in a more "usable" way for us, and then we display the content of this new array :
$sql = "SELECT categories.id as catid, categories.name as catname, products.id as prodId, products.name as prodName
FROM `categories`
INNER JOIN `products` ON categories.id = products.category
ORDER BY categories.id DESC";
$result = execute_select($sql);
$categories = array();
foreach($query as $row) {
if( ! isset($categories[$row['catid']])) {
$categories[$row['catid']] = array(
'id' => $row['catid'],
'name' => $row['catname'],
'products' => array(),
);
}
$categories[$row['catid']]['products'][] = array(
'id' => $row['prodId'],
'name' => $row['prodName'],
);
}
foreach($categories as $cat) {
echo '<li>' . $cat['name'] . '<ul>';
foreach($cat['products'] as $prod) {
echo '<li>' . $prod['name'] . '</li>';
}
echo '</ul></li>';
}
One step variant
We store the current category, and when the category changes, we close the current list and open a new one :
$sql = "SELECT categories.id as catid, categories.name as catname, products.id as prodId, products.name as prodName
FROM `categories`
INNER JOIN `products` ON categories.id = products.category
ORDER BY categories.id DESC";
$result = execute_select($sql);
$actualcategory = null;
foreach($query as $row) {
if($actualcategory != $row['catid']) {
echo '<li>' . $row['catname'] . '<ul>';
}
echo '<li>' . $row['prodName'] . '</li>';
if($actualcategory != $row['catid']) {
echo '</ul></li>';
$actualcategory = $row['catid'];
}
}
n+1 query
In this solution, we retrieve the list of categories, and then, for each one, retrieves the list of products :
$sql = "SELECT categories.id, categories.name
FROM `categories`
ORDER BY categories.id DESC";
$categories = execute_select($sql);
foreach($categories as $cat) {
echo '<li>' . $cat['name'] . '<ul>';
$sql2 = "SELECT products.id, products.name
FROM `products`
WHERE `products`.category = ".$cat['id'];
$products = execute_select($sql2);
foreach($products as $prod) {
echo '<li>' . $prod['name'] . '</li>';
}
echo '</ul></li>';
}
Dry coding warning
I dry coded the preceding piece of PHP code, I'm not even sure I didn't made some kind of silly mistakes. It is possible you will have to adapt them to your needs. If something is wrong, please point it out in the comments, I will fix it ASAP :)
Conclusion
The first two possibilities executes only one query and then parse the results to display meaningful information. The code is, in my opinion, fairly hard to understand and error prone.
The last possibility is much more clearer and, I think, easier to modify and extend.
From a performance point of view, we have only one query in the first two versions, but we retrieves much more data and a join in necessary. I think there's no easy answer about which solution is best, it will greatly depends on the number of categories and products for each of them. I think the best to do is to test each of the solution on various data set to determine the quicker one.
If I would have to develop this kind of menu, I will personally use the n+1 query approach. Even if the performance are slightly off (which I'm not sure), the solution is so much clearer that it compensates the weaknesses.
Disclaimer
Kudos to every other poster on this question, I didn't provide a "new" answer, I just put the already provided one in PHP code. Hope this will help !
You want two queries.
First get a list of categories, and loop through them. Then within each category do a second query and get the products.
(Yes, it's technically possible to get all the data in one query and check if the category was already output, but I recommend against it.)
SELECT
*, products.id as prodId, products.name as prodName
FROM
products
LEFT JOIN
categories ON categories.id = products.category
You want products table so take it FROM products not FROM categories
The SQL Grouping operator roughly works like this: You group by an attribute, and you (under the hood) make groups of rows which have the same value in that attribute. The query result will only contain the top of each those groups. You can use aggregate functions on each of those groups but that's about it.
You have two options:
(a) Either perform separate queries for each category (as previously suggested) or
(b) Using your first query, order by category and in your loop, while the current row is in the same category as the previous row then add it to your current list. When you reach a result which belongs to another category than the previous one then close the current list (echo </ul>;) and start a new one