Finding all non-empty categories in a tree - php

I have a categories table set up as so [id, name, parent_id] and a items table [id, name, category_id, visible]. What I'm trying to do is create a query that will return all the ids of all non-empty categories, non empty being that it or one of it's children has at least one item belonging to it. What would be the best way to do this in MySQL?
edit
SELECT DISTINCT category_id FROM Items
This works for categories containing items, but I also need the parent categories of all item containing categories. This query will be used as a subquery along with some other filters.
Top Level Category
->Second Level Category
-->Third Level Category
--->Item 1
--->Item 2

Maybe off topic, but I think it is still worth referencing: Extensive Article on Managing Hierarchical Data in MySQL.

All non-empty categories, and only those, have items with category_id pointing at them, therefore you could just select category_ids from items table:
SELECT DISTINCT category_id FROM Items
As far as I know, you can't select all the ancestors of these categories in one query, however you might want to use another tree model.
With the nested set model, your query could look like this:
SELECT DISTINCT c.id FROM Categories c JOIN Items ON c.id = category_id JOIN Categories ancestors ON c.lft BETWEEN ancestors.lft AND ancestors.rgt
I'm not sure if it'll work, but you can try.

Related

Mysql: Select from parent table, only if child table has rows

I need your help,
How do I Select from a parent table, only if child table (another table that depends on id of parent table) has rows in php?
For instance:
I have CATEGORY table and Items table. Where Category is the parent table and contains ct_id, ct_name, while Items is child table which contains it_id, ct_id (linked to parent table), it_name
PS: I don't need to select items, but I need only Categories ONLY if there are items linked to this table.
Thank you
You could use INNER JOIN which only returns records when there is a match on both tables.
SELECT DISTINCT a.* FROM Categories a INNER JOIN Items b on b.ct_id = a.ct_id
But it is more efficient to run a subquery:
SELECT *
FROM Categories
WHERE ct_id IN (SELECT ct_id FROM Items);
This is because in the first example it has to match the entire table first and then strip out all the duplicates using the DISTINCT keyword. The second example avoids the duplication by scanning the child table first.
This is known as a Semi Join. See here for more: https://dev.mysql.com/doc/refman/5.6/en/semi-joins.html

Joining three MS SQL Tables in PHP to display random products

I am trying to have a section in a shop I am creating to display 5 random products, from that Category ID.
Firstly every category has an ID, and a set of Sub Catrgories, every Sub Cat has an ID, and within every Sub Cat is a number of products. Every Product also has an ID.
The Products table contains the ProductID and the SubCatID.
The SubCat Table Contains the SubCatID and The CatID
The Cat table contains only the CatID.
SO I need to display 5 random products by the CatID. I can get random products using a query similar to this:
$randomprod = mssql_query("SELECT TOP 5* FROM Products WHERE SubCatID = '1' ORDER BY NEWID()");
while ($echorand = mssql_fetch_array($randomprod)) {
I need a way to join the tables so I can display all products under a certain CatID however, and am finding it difficult because my Products table doesn't contain a CatID. I am aware there are a number of joins, but am fairly new to PHP and even newer to MS SQL. Can anyone tell me what join is best, or point me in the correct direction please?
Join products to sub categories, join sub categories to categories, like this:
SELECT TOP 5 Products.Name, SubCatergory.Name, Category.Name
FROM Products
INNER JOIN SubCatergory ON Products.SubCatID = SubCatergory.SubCatID
INNER JOIN Catergory ON SubCatergory.CatID = Category.CatID
WHERE Category.CatID = 1
ORDER BY NEWID()
I have used INNER JOIN in the above example.

Order results from one MySQL table by another, which is saving multiple values into the order field

I'm trying to solve a MySQL problem, I have two tables:
CATEGORIES (COLUMNS: _id, _name)
POSTS (COLUMNS: _id, _category, _title, _text)
_category field in POSTS is LONGTEXT and can have multiple CATEGORIES _ID's separated only by , using implode(",") PHP function.
I try to list with PHP the 10 most popular categories, and to display in () the posts in them, but without luck.
I'm not very familar with MySQL, I only know how to use SELECT FROM WHERE ORDER LIMIT, INSERT & UPDATE so I will be very happy if someone can give me a good solution. I tried to use IN() but IN() needs the _category field of POSTS to be like this '1','2','3','4', now its 1,2,3,4 without the quotes, so if anyone know how I can transform this field into list without FIELD TYPE SET, I will be pretty happy.
You may want to change your relation model to the following:
Table CATEGORIES with columns:
_id
_name
Table POSTS with columns:
_id
_title
_text
Table POSSESS with columns:
post_id (FOREIGN KEY)
category_id (FOREIGN KEY)
A tuple in POSSESS relation (table) means the post_id is in the category_id category.
the key word for this is "many-to-many" relations, if possible refactor your scheme like Mark Baker wrote.
Using the model that Dyin suggested, you would then use something like this to list the top 10 categories by popularity (assuming that the more posts a category has, the more popular it is):
SELECT
c.*, # get all values for rows in categories
count(p.post_id) AS post_count # here we are counting the posts for each category using a field alias for the count
FROM (
categories AS c, # we are aliasing the tables also to shorten the typing a bit
possess AS p # you could also use aliases to join the same table multiple times
)
WHERE
c.id = p.category_id # link the categories and the possess tables
GROUP BY c.id # without this, the query would just count all posts, this way the result set is separated into groups by category
ORDER BY post_count DESC
LIMIT 10
Given what you said about your experience with SQL, this query might seem a bit over the top for now, but I think you could use as a starting point for learning more, as always, google is your friend. Start by researching how to link tables using foreign keys and joins.
I've used this:
SELECT *, (SELECT COUNT(*) FROM offer WHERE FIND_IN_SET(type._id, offer._type)) AS _count FROM type ORDER BY _count DESC LIMIT 0, 10
Works fine for now, its table type (columns: _id, _name) and offer (columns: .., .., _types,

Retrieving and Displaying Categories and their children count

I am using PHP via Codeigniter for this scenario.
It's a basic category listing that has items assigned with to those categories. What I'm having problem with is showing the item count beside the category names on the category listing page.
How I was doing it was a very ineffective way i think, and I would really like to know what would be the common practice for this sort of thing.
So I was getting the category list in my controller, and then in my view, on the loop thru the categories, I call a model method to grab the count of the respective cat_id on every iteration. (horror!)
Should I be:
A) Getting the categories AND it's item count from 1 SQL statement
OR
B) Process the category list in the controller and get the categories' corresponding item counts in the controller
If so, how to ?
I would prefer to have one query with everything I need, if your DB design allow to do so.
Assuming you have a Categories table and a Products table you should be able to do something like this:
Select C.Category, Count(P.Id) as ProductsCount
From Categories C left join Products P on C.Id = P.Category_Id
Group by C.Category
Order by C.Category

Multiple database joins

I have three tables: categories, content_info, and content.
The categories table contains the category's id and the ID of its parent category.
The content_info contains two columns: entry_id for the post's ID and cat_id for the ID of the post's category.
The content table contains multiple columns about the post - such as ID, title, etc.
I have a variable in the URL called parent_id which corresponds to the parent of a category. I want to list all the POSTS (not CATEGORIES) which belong to a category with a parent of the parent_id value.
For example, say the parent_id value is 5. Each post might belong to a category with an ID of 20, but that category belongs to the parent category (whose ID is 5). I want to list all the posts who belong to categories with a parent value of whatever the current parent_id happens to be.
Is there a way of doing this with MySQL joins instead of changing the PHP?
This should do it:
SELECT c.* FROM content
JOIN content_info ci ON ci.entry_id=c.id
JOIN categories cat ON cat.id=ci.cat_id
WHERE cat.parent_id=<parent_id>
This return all posts (content rows) which belong to a category which parent is parent_id
Or with subqueries:
SELECT c.* FROM content
JOIN content_info ci ON ci.entry_id=c.id
WHERE ci.cat_id IN (SELECT id
FROM categories cat
WHERE cat.parent_id=<parent_id>)
SELECT c.*
FROM content c,
categories cat,
content_info ci
WHERE c.id = ci.entry_id
AND cat.id = ci.cat_id
AND cat.parent_id = 5

Categories