Hello i am in a delima
Suppose that i have 50 products in a category and i want to get all the features of my products...
Ofcourse a SQL join wont help because joining would return product info many times!
So here is the question.. what is better
PLAN A
Get all the products' features in category with one SQL query and then iterate with php each feature and put it in Product.
PLAN B
For each product get the features by calling a query
Other solutions accepted!
EDIT
My table schema outline is..
A table Product which has product info(per row)
A table features which has features (feature id )
A table for features' values
And a table that has Products with their features and values
$sql1 = "SELECT * FROM products P, ". //don't use star, make sure no fields are overwritten
INNER JOIN products_to_features PTF on P.id = PTF.project_id
INNER JOIN features F F.id = PTF.feature_id
ORDER BY P.id";
$r = mysql_query($sql1, $conn);
$arr = array();
$lastProductId = -1;
while ($row = mysql_fetch_assoc($r))
{
if ($row[p_id] != $lastProductId)
{
$lastProductId = $row['p_id'];
$arr['p_id'] = array('productName' => $row['p_name'],
'productPrice' = $row['p_price'],
'productFeatures' = array(),
//other fields from product table);
}
$arr['p_id']['productFeatures']['f_id'] = array('featureName' => $row['f_name'], blah...);
}
I don't know your fields obviously, and you may want to join on feature_values so that will be more work. You can do keys/values different (ie - product names as keys. Feature-name as keys with feature-value as values, whatever you want) but the point is this is doable (and recommended) in one query.
Not Plan B.
Whatever you do this can and should be done with one or at most two total queries (one for headers, one for correctly sorted list of features + id column). Whether that query is Plan A or some unmentioned Plan C depends on your exact table structure, which isn't clear from your question.
Generally, the less database queries you have to make, the better. It greatly depends on your table structure, but I'd go with Plan A.
A query inside a loop will greatly degrade performance of your application. Avoid it at all cost.
Show us the schema. There's a strong possibility a single SQL query will solve your problem.
Related
I'm making a simple option-based adventure game using PHP and a mySQL database in phpMyAdmin. I've got it set up so that events in the game are objects in a table called Events. The options you can choose at each event are in a table called Actions. The two have a many-to-many relationship, because some actions may be taken at multiple events. Therefore, I've created a junction table called EventActions. I can successfully query which actions should be available for an event with an inner join using that junction table:
public static function getActions($eventID){
$db = Database::getDB();
$query = 'SELECT * FROM eventactions ea
INNER JOIN actions a ON a.actionID = ea.actionID
WHERE ea.eventID = :eventID';
$statement = $db->prepare($query);
$statement->bindValue(":eventID", $eventID);
$statement->execute();
$actions = $statement->fetchAll(PDO::FETCH_ASSOC);
$statement->closeCursor();
return $actions;
}
However, the situation gets sticky when I try to incorporate needing certain items to do certain actions. I'm also trying to incorporate needing to have done certain things for certain actions to show up. This has led me to make tables for Items, and Effects, and junction tables for itemActions and effectActions. So now my database query function needs to be much more complicated, and I'm having trouble wrapping my head around forumlating the query. I need a query that essentially gets from the database: All actions that are tied to the event given, and the items and worldeffects columns that are tied to that action. I attempted something like this, but it does not return any rows:
select * from eventactions ea
inner join actions a on a.actionID = ea.actionID
inner join actionitems ai on ai.actionID = a.actionID
inner join actioneffects ae on ae.actionID = a.actionID
inner join worldeffects e on e.effectID = ae.worldEffect
where ea.eventID = :event
What I want returned from this function is a list of all of the options that are available for a given event. For each option, I want the columns from the Action table, from the Items table (so I know which items this option requires) and from the Effects table (so I know what effect on the world an option has, and what world effects are required for an option to be available).
Let me know if I'm not including necessary information. Thanks!
I am using codeigniter and MySQL to build an ecommerce web application.
This one required three level of categories. So I have created 3 tables. These are-
category
category_id, category_name
subcategory
subcategory_id,subcategory_name,subcategory_category_id
subsubcategory
subsubcategory_id,subsubcategory_name,subsubcategory_subcategory_id
Here they are linked as parent of one another. Finally I have the product table
product
product_id, product_name, product_subsubcategory_id
Now, I need a sql query on this to fetch all product of any specific category.
Something like
$this->Mdl_data->select_products_by_category($category_id);
Please help me on this. I have tried PHP programming to solve this. But it was too slow with lot's of nested loops.
If you need to select all products, that match some specific category, try this request:
SELECT p.product_id, p.product_name, p.subsubcategory_id FROM category c
JOIN subcategory sc ON sc.subcategory_category_id = c.category_id
JOIN subsubcategory ssc ON ssc.subsubcategory_subcategory_id = sc.subcategory_id
JOIN product p ON p.subsubcategory_id = ssc.subsubcategory_id
WHERE c.category_id = 1;
But you should think about changing your database structure to make your requests faster and simpler.
Edit: Answering the comment about how to improve DB.
Current design of database looks correct, according to actual data relations. 1-many for cat-subcat and 1-many for subcat-subsubcat. But this leads to complicated (and possibly slow) queries while usage.
One way I see is to implement many-many relation with additional restriction. You can create additional table cat-subcat, just as you would do if you needed many-many. But in that table you can set unique limitation to subcat_id, so every subcat could belong only to 1 cat and it becomes in fact 1-many relation. But in this case you can move both up- and downwards the hierarchy. This approach will reduce number of JOINs in your query only by 1, but the whole logic of the query would be easier to understand.
Another way. As I understand this is the query for web-store filter. So, new products will be inserted much more seldom, than viewed by category. You can just add subcat_id and cat_id fields to your product, which is not good idea from the point of data structure, but for this particular situation this might be good solution. Every time new product is inserted to DB, you should control the correctness of those 2 fields by PHP or whatever you use on server. But when products are searched by category you will have simple request without JOINs at all.
Both approaches are based on the idea to sacrifice some space for speeding up and simplifying the queries, that are frequently used. Maybe there is even better solution, but I can't find it right now.
I'm working on a fairly large project in (object oriented) PHP which includes a webshop where users can buy products and additional licenses and services. Purchasing is a four step process:
Step 1: User chooses a product, and product_id is passed to step 2
Step 2: Fetching and outputting all license types based on product_id, passing product_id on to step 3
Step 3: Fetching and outputting all services based on product_id, in a form with checkboxes named services[$service_id]
So now, on the checkout on step 4, I fetch the correct products and licenses from the database, but I also have to get the correct services from the db based on the services array, and calculate the price to output. At last, I'll have to assign the services array to the Smarty template.
How would the most suitable way to do this be? I really hope that someone is kind enough to help me out here, as I'm having a very hard time trying to make this work.
Thank you so much in advance.
Try using
$resulter = array();
$i = 0;
foreach($product_id as $value) {
$query = "Select FROM products WHERE product_id = $value";
Your execution code
$resulter[$i] = $result; //$result could be a assoc array
$i++
}
And If I ware you I would i would use a multidimensional array like I shown above.
Sounds like you need a JOIN.
Something like a JOIN on the 'products' table and 'licences' table using the 'product_id' field to make the join.
An example query would be something like:
SELECT <required_fields_here>
FROM products
JOIN licences ON licences.product_id = products.product_id
WHERE product_id = <product_id_here>
Note that in the SELECT section you can select fields from both 'products' and 'licences' tables, you just need to prefix with the table and a dot e.g. 'product.product_id'
I think you will need to be more specific if you need further help. Hope it helps.
I have a Product class that when instantiated, fetch data from "product" table, it also creates a "Category" object inside it (product->category) that fetch data from the category table. My problem is, what happens when multiple products from the same category are listed, multiple equal queries are made in order to create the category object, since they're from the same category. How to solve this?
Use a JOIN in your SQL:
SELECT *
FROM product
LEFT JOIN category ON product.category_id = category.id;
You'll have to build a layer that intercepts and/or caches your queries. When it sees you're requesting category with a certain ID, it should present that category from its cache, and if it isn't there, it should retreive and then cache it, so during a request the same row won't be queried more than once.
Doing this manually is a no-go, simply pick a decent ORM that will do this for you.
Use factory that creates the objects, and provide the option the data for product/category to be passed in the constructor. The factory loads the data for all products that will be listed, than instantiate the product objects, providing the already fetched data
Inside the loop executing sql query is not good practice.
What you can do is left join product table with the category tabel.
$select = SELECT *
FROM product
LEFT JOIN category ON product.category_id = category.id;
Then execute query and then classify your products into category basis
like you can run loop
$result = mysql_query($select);
while($data = mysql_fetch_assoc($result)){
$product_info['product_name'] = $data['product_name'];
$product_info['price'] = $data['product_price'];
$product[$data['category']][$data['product_id']] = $product_info;
}
This loop will do the magic that it will help you to classify products falling under same category.
And you can display your data in more effective way.
I'm really hoping someone can help me with this. I have a number of product attribute types that users can select from to refine the products that are returned to them on screen. What I'm trying to do is, for each product attribute type, I want to list all attributes that relate to either the selected category or search term, then once they've made their selections, I still want to display each of the attributes that relate to the category or search term, but only display a clickable link if the product count for that particular attribute is greater than 1 and for those that have a product count of zero, I want to list them, but make them unclickable. An example of what I'm trying to achieve can be found on the ASOS website, in the left hand menu
http://www.asos.com/Women/Dresses/Cat/pgecategory.aspx?cid=8799#state=Rf961%3D3340%2C3341%40Rf-200%3D20&parentID=Rf-300&pge=0&pgeSize=20&sort=-1
Initially I tried using just joins to achieve this, but I wasn't able to do it, successfully. So I decided to create a temporary table for each attribute type which held a list of all the attributes that related to the main query and then created a refined query, with a left join. Here's my code:
CREATE TEMPORARY TABLE temp_table
SELECT su_types.id, type AS item FROM su_types
INNER JOIN su_typerefs ON su_types.id=su_typerefs.id
INNER JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
WHERE wp_category_id =40 GROUP BY su_typerefs.id
$sudb->query($query);
if ($sudb->affected_rows > 0) {
SELECT temp_table.id,item,COUNT(su_typerefs.mykey) AS product_count FROM temp_table
LEFT JOIN su_typerefs ON temp_table.id=su_typerefs.id
LEFT JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
LEFT JOIN su_stylerefs ON su_pref.mykey = su_stylerefs.mykey
LEFT JOIN su_productrefs ON su_pref.mykey = su_productrefs.mykey
WHERE wp_category_id =40 AND su_stylerefs.id in (91) AND su_productrefs.id in (54) AND su_typerefs.id in (159) GROUP BY su_typerefs.id
if ($itemresults = $sudb->query($query)) {
while($itemresult = $itemresults->fetch_array(MYSQLI_ASSOC)) {
$id=$itemresult['id'];
$item=$itemresult['item'];
$product_count=$itemresult['product_count'];
build_link($list_type, $item, $product_count, $id);
}
}
In the above example the first query selects all the product types that relate to a particular category, say dresses. And the second query is based on the refinements the user has made on the category, in this example this is product, product type and style. A user can also refine their search by colour, fit, fabric and design.
There are a couple of issues with this:
1) The number of results returned in the second query do not match the results of the first. Using the above as an example, I wish to list all products that relate to the chosen category, then using the second query return the product count for each of these products as I described above. So if the temporary table returns, trousers, jeans and skirts. I expected these three items to be displayed on screen based on the conditions applied in the second query, however my results may only show trousers and jeans, if there is not a match for skirts in the second query. I thought that using a left join would mean that all the results of the temporary table would be displayed.
2)Also I wonder if I'm doing this the most efficient way. I have a total of 8 attribute groups, and therefore need to do the above 8 times. If the user choses to refine the results using all 8 attribute groups then in addition to the temp table join, there will be a total of 9 joins for each type. It's taking a while to execute, is there a better way to do this? There are approximately 1/2 million products in the table, and this will probably be 5 times this, once my site goes live.
I really hope all that I have written makes sense and I'd really appreciate the stackoverflow community's help with this, if anyone can help. I apologise for the essay ;). Thanks in advance
To answer your first question; yes, a LEFT JOIN will indeed keep all data from the initial table. That, however, isn't the problem.
The reason why you lose empty categories, is most likely (I say this because I don't fully know your db structure) because of the where condition filtering out all results based on the data in the joined tables.
If for a category all items get filtered out (possibly including the NULL joined values), you will not get this category back from that query anymore. Also the GROUP BY is done on a joined column, that might also effectively wipe out your other categories.
As for the second question, you already state it's taking long; so it's probably not the way to go if you want things to work fast ;) (okay, obvious answer, low hanging fruit, etc). What you might want to do, is get a collection of keys from the filterable categories first, and use that data to select items.
This prevents that you have to join up your entire products table in a temp table (at least, that's what I think you're doing), which of course will take long with the given number of entries. Selecting a list of matching IDs from the given attributes also gives you the advance of using your indexes (more), which a temp-table probably won't have. If this is possible and feasible mainly depends on your schema's structure; but I hope it might lead you to the direction you want to go :)