How do I represent a Tree Structure in a mySQL table - mysql

The tree has the following characteristics:
Each node can have multiple parents and multiple children.
The Parent nodes of a Node can have different depth.
Example
I am trying to represent a category structure such as the following:
Desktop and Mobile Applications
Desktop and Mobile Applications->Android Apps
Desktop and Mobile Applications->Android Apps->Games
Desktop and Mobile Applications->Android Apps->Games->Action
Desktop and Mobile Applications->Games
Desktop and Mobile Applications->Games->Action
Desktop and Mobile Applications->Games->Adventure
Desktop Applications
Desktop Applications->Games
Desktop Applications->Games->Action
Desktop Applications->Games->Adventure
IPhone Applications
Desktop Applications->Games
Desktop Applications->Games->Action
Desktop Applications->Games->Adventure
Tried using the Nested Set Algorithm and I end up with multiple "Games" categories with different categoryIDs and at different depths.
Any help with this will be much appreciated.

The simple way is to structure a table like:
Categories
CategoryID
ParentID
Name
Your data would look like:
1, 0, 'Desktop and Mobile Apps'
2, 1, 'Android Apps'
3, 2, 'Games'
4, 3, 'Action'
5, 1, 'Games'
6, 5, 'Action'
7, 5, 'Adventure'
8, 0, 'Desktop Apps'
9, 8, 'Games'
You would query it like:
select * from Categories where ParentId = 1 which would return Android Apps and Games. To get the sub categories of games you would do select * from Categories where ParentId = 5 which would return action and adventure.
update
In order to associate a single item with multiple categories you will want one additional table:
xref_CategoriesItems
CategoryId
ItemId
This would allow any single item to be associated with multiple categories. Let's say you have a desktop app that needs to appear with both Desktop Apps > Games and Desktop and Mobile Apps > Games.
Your table would have the following data for item 1:
3, 1
9, 1
When seeing what items were in a specific category you would do the following:
select I.*
from items I
inner join xref_CategoriesItems XCI on (XCI.ItemId = I.ItemID)
WHERE (XCI.Category = #CategoryId)
To see which categories a specific item falls under:
select C.*
from categories C
inner join xref_CategoriesItems XCI on (XCI.CategoryId = C.CategoryId)
where (XCI.ItemId = #ItemId)
The query for all items under a specific category is a little more complex if you need all of the child records. Basically you need to do a recursive join the xref_categories with the categories to get the children. I don't remember how to express that in MySQL's version of sql; however the following might be good to know: Using MySQL query to traverse rows to make a recursive tree

What you are asking for is not really a tree, but a graph. Especifically it's a Directed Acyclic Graph (DAG). Here it's a link speaking about storing DAGs in a relational database.
It might be easier to just go with a traditional tree structure for the categories, and allow for items to be in multiple categories with the help of a item/category linking table.

An alternative approach would be to use tags on whatever items you are storing instead of categories. From the example you give, that may even be more appropriate - instead of "Desktop and Mobile Applications", "Desktop Applications" and "Mobile Applications", you would use the "Desktop" and "Mobile" tags. An item with both would then naturally fall into the first category.
Ref: How to store tags in MySQL tags, one field in total or one filed for each tag?

Related

Magento how to add a product with dynamic pricing capability

I have to display a product on magento 1.9 which has 5 distinct attributes that determine price, namely:
1, Size (9,14,18)
2, Metal (Gold, Silver)
3, Gender (Women, Men)
4, Stone (Diamond, Swarovsky)
I have made the attribute(configurable) for each and added to attribute set. Each one of these would alter the price based on drop down selection.
How can i make it work making it dynamic price such as the price will change dynamically based on each drop down selection. for example:
Blockquote
1, Size->Metal->Gender->Stone
2, 9->Gold->Set->Diamond = $5000
3, 9->Gold->Set->Swarovsky = $3000
4, 9->Silver->Women->Diamond = $4500
Blockquote
So we have multiple options here for price selection. Is this something magento can handle or only option is to use a third-part extension.
Do you know any free third-party extension for this or any way it can be coded to upload xml,csv files of these combination allowing the price to change every time the user selects different option.
Thanks.

Laravel 4 Eager Loading filtering and selecting only matching results

I'm trying to output the filter results with only matching elements.
I have two tables (in the real project, which will be 5), let's say companies and projects. A company may have more than one project or may not have any.
These are the relations:
/app/models/Company.php
<?php
class Company extends Eloquent {
public function projects() {
return $this->hasMany('Project','companyID');
}
protected $table = 'companies';
}
/app/models/Project.php
<?php
class Project extends Eloquent {
public function companies() {
return $this->belongsTo('Company','companyID');
}
}
What I want to do is, I want to get results of them both but only with matching parameters.
I've tried this:
return Company::with(array('projects'=>function($query){
$query->where('id',99); //project's id is 99
}))->get();
This is the output JSON
If I change the value from 99 to 1 (there is a result with products.id of 1), it changes into this:
I only want to get the second result from the second JSON i've posted.
As you can see in the second JSON (I'm using this parser to check), all companies are loaded regardless of the project, and only the rows matched have the object projects.
There will be more 'with's and I don't know how to filter only matching elements.
I also tried having() inside closure, but it's still same:
$query->having('projects.id','=',99);
Is there a way to filter only matching results (without using a loop) which the output will only include the results having the matched projects object?
Edit: Actually, 5 tables will be filtered.
companies, projects, works, users and user_works
Let's say;
"Companies" have many projects.
"Projects" have many works
"Works" have many users, also "Users" may have more than one work (pivot table user_works).
All relations are set correctly from models.
I want to do a global searching to these.
Something like: "Bring me the user id 1's works which has company id of 5 and project id of 4", but none of the fields are mandatory.
So these are also valid for searching: "Bring me everyone's works on project id of 2", or "bring me id 2's works", or "bring me all the works starting from today", "bring me the id 1's works on project 2", "Bring me this year's works done of company id 1".
Thanks in advance
Using whereHas() is the solution on my case. It filters relation and affects the results returned in the main query.
If you have the id of the project, would it make more sense to go that route? $project = Project::find(99); and then the company variables would be accessible with $project->companies->name;
It would make more sense to rename the companies() function to company() because a project will only ever belong to one.

select by order

Thare are two tables: image and car. They are linked by one-to-many unidirectional relationship.
I want to make something like gallery: simultaneously load only one photo of a car, and other photos are available via links [1], [2], [3], ...
Question: but how can I pull photo by it order. It means I don't know image's ids (they may be 10, 23, ....) and/ for example, I don't know which image I should pull via [3] link.
P.S. I pull images via command: SELECT * FROM image WHERE car_id_fk=?
use limit - keep the second number at 1, and for the first number put the selected link - 1
select * from image where card_id_fk=? limit 0,1

Codeigniter database call efficiency

This is an efficiency/best practice question. Hoping to receive some feed back on performance. Any advice is greatly appreciated.
So here is a little background in what i have setup. I'm using codeigniter, the basic setup is pretty similar to any other product relationships. Basic tables are: Brands, products, categories. On top of these tables there is a need for install sheets, marketing materials, and colors.
I created some relationship tables:
Brands_Products
Products_Colors
Products_Images
Products_Sheets
I also have a Categories_Relationships table that holds all of the relationships to categories. Install sheets etc can have their own categories but i didn't want to define a different category relationship table for each type because i didn't think that would be very expandable.
On the front end I am sorting by brands, and categories.
I think that covers the background now to the efficiency part. I guess my question pertains mostly to weather it would be better to use joins or to make separate calls to return individual parts of each item (colors, images, etc)
What I currently have coded is working, and sorting fine but I think i can improve the performance, as it take some time to return the query. Right now its returning about 45 items. Here is my first function it grabs all the products and its info.
It works by first selecting all the products and joining it's brand information. then looping through the result i set up the basic information, but for the categories images and installs i am using functions that returns each of respected items.
public function all()
{
$q = $this->db
->select('*')
->from('Products')
->join('Brands_Products', 'Brands_Products.product_id = Products.id')
->join('Brands', 'Brands.id = Brands_Products.brand_id')
->get();
foreach($q->result() as $row)
{
// Set Regular Data
$data['Id'] = $row->product_id;
$data['Name'] = $row->product_name;
$data['Description'] = $row->description;
$data['Brand'] = $row->brand_name;
$data['Category'] = $this->categories($row->product_id);
$data['Product_Images'] = $this->product_images($row->product_id);
$data['Product_Installs'] = $this->product_installs($row->product_id);
$data['Slug'] = $row->slug;
// Set new item in return object with created data
$r[] = (object)$data;
}
return $r;
}
Here is an example of one of the functions used to get the individual parts.
private function product_installs($id)
{
// Select Install Images
$install_images = $this->db
->select('*')
->where('product_id', $id)
->from('Products_Installs')
->join('Files', 'Files.id = Products_Installs.file_id')
->get();
// Add categories to category object
foreach($install_images->result() as $pImage)
{
$data[] = array(
'id' => $pImage->file_id,
'src' => $pImage->src,
'title' => $pImage->title,
'alt' => $pImage->alt
);
}
// Make sure data exists
if(!isset($data))
{
$data = array();
}
return $data;
}
So again really just looking on advice on what is the most efficient, best practice way of doing this. I really appreciate any advice, or information.
I think your approach is correct. There are only a couple of options: 1) load your product list first, then loop, and load required data for each product row. 2) create a big join on all tables first, then loop through (possibly massive) cartesian product. The second might get rather ugly to parse. For example, if you got Product A and Product B, and Product A has Install 1, Install 2, Install 3, and product B has Install 1, and Install 2,t hen your result is
Product A Install 1
Product A Install 2
Product A Install 3
Product B Install 1
Product B Install 2
Now, add your images and categories to the join and it might become huge.
I am not sure what the sizes of your tables are but returning 45 rows shouldn't take long. The obvious thing to ensure (and you probably did that already) is that product_id is indexed in all tables as well as your brands_products tables and others. Otherwise, you'll do a table scan.
The next question is how you're displaying your data on the screen. So you're getting all products. Do you need to load categories, images, installs when you're getting a list of products? If you're simply listing products on the screen, you might want to wait to load that data until user picks a products they are viewing.
On a side note, any reason you're converting your array to object
$r[] = (object)$data;
Also, in the second function, you can simply add
$data = array();
before the foreach, instead of
// Make sure data exists
if(!isset($data))
{
$data = array();
}
You can try this:
Query all of the products
Get all of the product IDs from step 1
Query all of the install images that has a product ID from step 2, sorted by product ID
Iterate through the products from step 1, and add the results from step 3
That takes you from 46 queries (for 45 products) to 2 queries, without any additional joins.
You can also use CodeIgniter's Query Caching to increase performance even further, if it's worth the time to write the code to reset the cache when data is updated.
Doing more work in PHP is generally better than doing the work in MySQL in terms of scalability. You can scale PHP easily with load balancers and web servers. Scaling MySQL isn't as easy due to concurrency issues.

PHP/MySQL - Changing Query on clicking a link

I have a very basic site (just learning this stuff) where I have the results for a hockey pool I do from here.
I want to build a page that lists the players and then when they click on a name, a page comes up with just that player's picks on it. You can see the kind of page I mean here.
THis page currently only shows one player, as I am using this query:
$q = "SELECT *, player_id, handle
FROM round_one
INNER JOIN players USING (player_id)
WHERE (player_id = 1)";
Right now I have it set just to return player_id 1, but I want it to do player_id 2, 3, 4 etc depending on what linked name they click on.
I have been reviewing some documentation on sort by columns queries when clicking on a column header, and am thinking this is sort of the approach I should be taking, but I just can't seem to figure out the correct way to do it.
You can achieve this by passing a parameter to the view_picks_by_player.php page, and then PHP can pick up that parameter and generate content for the appropriate player.
For example, in the page where you select the player you want stats for you could have:
<a href='view_picks_by_player?id=player1'>Get Player 1's Stats!</a>
<a href='view_picks_by_player?id=player2'>Get Player 2's Stats!</a>
And then in the top of the view_picks_by_player.php page you could have:
$id = $_GET['id'];
$sql = "SELECT * FROM player_stats WHERE id = '$id'";
Obviously your SQL is structured differently, but this is just a quick way to show how you can pass a variable to the php page so it can generate different content dynamically.
For security, you'll want to validate that the user has not typed anything nasty into the URL as the 'id' before you use it in your SQL, but that's another topic...