I'm creating an e-commerce site where I have the category_id in the URL and based on the category_id I'll query all the children and grandchild (if found) so I can display products only related to that category_id. This query is really cool because it doesn't care about how many parent's it has ... it just looks for all the parents until it doesn't find any more.
The problem I'm having is my query will only get parent and grand parents (if found), but I cannot seem out how to make this query get all children rows or grandchildren if found. This query was pulled from here, so I did not write the query and it's hard for me to make the correct changes ... I tried all day yesterday.
Edit
This query will also NOT get ALL the related children and grandchildren (if found). Where I want ALL children and grand children (if found).
Edit
Here's the SQL Fiddle where my data can be found.
SELECT T2.category_id, T2.category_name
FROM (SELECT #r AS _id, (SELECT #r := parent
FROM categories
WHERE category_id = _id) AS parent
FROM (SELECT #r := 182)vars, categories h
WHERE #r <> 0) T1
JOIN categories T2
ON T1._id = T2.category_id
ORDER BY category_id ASC
Here's my table schema:
category_id | category_name | parent
There is an alternate way to accomplish this, but it requires a second hierarchy table.
The additional table contains one row per parent-direction relationship, as in:
category_id category_name parent
1 cat1 1
2 cat2 1
3 cat3 1
4 cat4 2
5 cat5 3
6 cat6 5
category_id ancestor
1 1
2 1
3 1
4 1
4 2
5 1
5 3
6 1
6 3
6 5
This table can be maintained by your code or by a trigger. It makes selecting an entire hierarchy trivial and very fast, but adds some maintenance (addition/deletion) overhead, so you'll need to consider the long-term costs vs overhead.
If you know in advance the maximum possible amount of depth like #rambocoder indicated…
SELECT
p.`category_id`,p.`category_name` AS 'parent',
c.`category_name` AS 'child',
gc.`category_name` AS 'grandchild'
FROM (SELECT * FROM `categories` WHERE `parent` IS NULL) p
INNER JOIN `categories` c
ON c.`parent`=p.`category_id`
INNER JOIN `categories` gc
ON gc.`category_id`=c.`category_id`
WHERE p.`category_id`=120
ORDER BY c.`category_id`, gc.`category_id`
Otherwise if you're not sure how many levels deep the result might get, then you will need to write a stored procedure.
Related
I have table:
id | parent | regno | person
1 | 0 | 12 | 5
2 | 1 | 12 | 15
3 | 0 | 13 | 5
4 | 0 | 14 | 6
I have MySQL query...
SELECT *
FROM table
WHERE person='5';
...that returns rows 1 and 3.
In this table row 1 and 2 are related (same regno).
How can i build this query to include related rows?
Basically when searching for person 5 i need MySQL query to return following:
id | parent | regno | person
1 | 0 | 12 | 5
2 | 1 | 12 | 15
3 | 0 | 13 | 5
Parent column has id of column it is related to, but it can be positive and negative integer. All related rows always have same regno.
Thank you.
You want all people who have a regno that is the same as the regno of anyone who is person 5:
--this main query finds all people with the regno from the subquery
SELECT *
FROM table
WHERE regno IN
( --this subquery finds the list of regno
SELECT regno
FROM table
WHERE person = '5'
)
There are other ways to write this; i'm not a fan of IN, and personally would write it like this:
SELECT t.*
FROM table t
INNER JOIN
(
SELECT DISTINCT regno
FROM table
WHERE person = '5'
) u
WHERE t.regno = u.regno
But it's harder to understand, and it's quite likely that these queries would end up being executed identically internally anyway. In this form the DISTINCT is required to make the regno from the subquery unique. If it were not, joined rows would end up duplicated. Why do I prefer it over IN? In some database systems IN's implementation can be very naive and low performing. "Never use IN to create a list longer then you would write by hand" is an old mantra I tend to stick to. This join pattern is also more flexible, can work with multiple values. Not every database supports Oracle-esque where x,y in ((1,3),(3,4)) value multiples
As an aside (and partly in response to the first comment on this answer) it would be more typical and more useful/usual to have the database prepare a set of rows that had parent and child data on the same line
It would look more like this:
SELECT *
FROM
table c
LEFT OUTER JOIN
table p
ON c.regno = p.regno AND p.parent = 1
WHERE c.person = '5' AND c.parent=0
This is assuming your "parent" column is 0 1 indicating true false.. you seem to have made a comment that parent is the id of the relative (not sure if it's parent-of or parent-is)
For a table where there is an id, and parentid column, and the parentid is set to a value when the row is a child of that other id;
id, parentid, name
1, null, Daddy
2, 1, Little Jonny
3, 1, Little Sarah
That looks like:
SELECT *
FROM
table c
INNER JOIN
table p
ON c.parentid = p.id
WHERE p.parentid ID NULL
Rows can have only one parent. A NULL in the parent id defines the row as being a parent, otherwise it's a child. You could turn this logic on its head if you wanted, call the column isparentof and have all child rows with null in the isparentof, and anyone who is a parent of a child, out the child id in isparentof. This then limits you to one child per multiple parents (single child families).. the query to pull them out is broadly the same
You can get all the id values for the person = '5' in a Derived Table.
Now, join back to the main table, matching either the absolute of parent (to get the child row(s)) or the id (to get the parent id row itself).
Based on discussion in comments, Try:
SELECT t.*
FROM your_table AS t
JOIN
(
SELECT id AS parent_id
FROM your_table
WHERE person = '5'
) AS dt
ON dt.parent_id = ABS(t.parent) OR
dt.parent_id = t.id
It is hard to comprehend though, why would you put negative values in parent!
I have looked at similar questions, but I can't seem to wrap my head around how the answers work in order to apply them to my case.
I have sets of articles (set_table)
ID SET ID ART
1 1
1 4
2 1
2 4
3 2
1 3
Those articles have a table with their parent ID. (article_table)
ID ART ID PARENT
1 1
2 3
3 2
4 1
Then those parents have a condition they have to meet, but it could be multiple (parent_table):
PARENT ID GROUP ID
1 6
2 15
3 12
Meaning, I have to select all sets whose articles (all of them) are in GROUP 6, then the result should be ID SET: 2. Or I could need to select all sets whose articles (all of them) are in GROUPS 6 and 15, then the result should be ID SET: 1. Or I could need to select all sets whose articles (all of them) are in GROUPS 6, 12; then the result should be NULL.
I have tried:
SELECT parent_id
FROM parent_table
WHERE group_id IN (6,15)
GROUP BY parent_id
HAVING COUNT(DISTINCT group_id) = 2; -- Number of group ids
Which is cool, but I don't manage to filter the sets correctly, my attempts in selecting the set are not working.
The query below is not so painful once you start writing it. Just join together the three tables, and then use conditional aggregation to count the number of entries in an ID_SET which have the desired groups.
The following query finds the ID_SET values which have groups of either 6 or 12. Note that this will return an empty result set for the sample data you gave in your original question. The DISTINCT subquery is needed to remove duplicates group values which would otherwise throw off the conditional aggregation.
SELECT t.ID_SET,
SUM(CASE WHEN t.GROUP_ID IN (6, 12) THEN 1 ELSE 0 END) AS groupCount
FROM
(
SELECT DISTINCT s.ID_SET, p.GROUP_ID
FROM set_table s
INNER JOIN article_table a
ON s.ID_ART = a.ID_ART
INNER JOIN parent_table p
ON a.ID_PARENT = p.PARENT_ID
) t
GROUP BY t.ID_SET
HAVING groupCount = 2 -- change 2 to however many group values you want to match
Use the below query
select ID ART
from articles a
join Parent1 p1 on a.ID ART = p1.ID ART
join Parent2 p2 on p1.ID PARENT = p2.PARENT ID AND p2.GROUP ID in (6,12)
i have searched for questions like this and although many are similars, are not answering exact my questions and queries not work.
Assuming we have the following table
id category_name parent_id
------------------------------------
1 test 0
2 test1 0
3 new_cat 1
4 new_catx 2
5 cat5 1
I want an sql query where the output will be like this
id category_name parent_id
------------------------------------
1 test 0
3 new_cat 1
5 cat5 1
2 test1 0
4 new_catx 2
In sort the output query is sorted based on parent_id. The parent_id = 0 is the root category, then child are following, and then again another parent with it's child. etc
This will work for a 1-level tree, i.e. for a tree containing only parents and their children:
SELECT *
FROM mytable
ORDER BY CONCAT(IF(parent_id=0, '', parent_id), id)
Demo here
Ideally you should use a mapping table for this, to make your solution cleanly scalable. Change your original table struct to
id category_name
---------------------
1 test
2 test1
3 new_cat
4 new_catx
5 cat5
...and have a parent mapping table, like so:
id parent_id
---------------------
3 1
4 2
5 1
Then you would simply modify Giorgos Betsos' excellent query to read:
select t.id, t.category_name, p.parent_id
from testtable t left outer join parents p
on t.id = p.id
ORDER BY CONCAT(IF(p.parent_id is null, '', p.parent_id), t.id);
I have a table categories with the following fields:
id, content, is_subcategory, topic_id
Here comes an examplatory set of data:
id content is_subcategory reference_id
=============================================
1 Games 0 989898989
2 Xbox 1 1
3 Playstation 1 1
4 Furniture 0 121212121
5 Sofa 1 4
6 Closet 1 4
7 Music 0 989898989
8 Pop 1 7
9 Reggae 1 7
Explanation:
If the category is a subcategory, its reference_id is the id of the parent category. For example, Sofa has 4 as its reference_id as 4 is the id of Furniture.
If the category is a parent category, its reference_id is the id of another table topics. For example, Music is a parent category and has 989898989 as its reference_id which is the id for the topic "entertainment".
How do I achieve that I can select only those subcategories whose parent category has 989898989 as its reference_id?
You can do just like it was 2 different tables:
select * from categories c, categories parent
where c.reference_id=parent.id and parent.reference_id=989898989 and c.is_subcategory = 1
SELECT * FROM category sub, category super WHERE
sub.reference_id = super.id AND super.reference_id = 989898989
AND sub.is_subcategory = 1
Since you alredy figured out that you need a self join for this question I am sure you can understand my solution. If not, just let me know.
Give different aliases and try
SELECT id,content FROM `categories` Child INNER JOIN
`categories` Parent ON Child.`reference_id` = Parent.`id`
WHERE Parent.reference_id=989898989 and Child.is_subcategory = 1
I like to suggest inner join for better perfomance.
I'm facing a problem while trying to retrieve all productIDs from a table if they match all items in an array, in this case, return products only if they contain every ingredient the user searched for.
Table looks like this
ID produktID ingredientID
----------------------------
1 418 1
2 418 2
3 418 3
4 416 4
5 411 1
6 411 5
7 411 6
I join this table from a products table where the main information is stored. The aim of the query should be to retreive a productID only when all ingredientIDs match with the given array. I've tried using WHERE ingredientID IN(1,5,6) but it always turns out to be an OR statement, returning every ID where any of the ingredients are matched.
So for example, if I pass (1,5,6) or (5,6) the product ID 411 should be returned, but if I pass (2,5,6) it should not.
The query I tried looks like this (simplified, it's part of a 5 way join to other relations like brands and catgories)
SELECT productID FROM products_ingredients_mm WHERE ingredientID IN (1,5,6) GROUP BY productID
but the result contains 418 aswell. How do I get it to match?
I hope I was able to describe the problem in an understandable way, it's really hard for me to wrap my head around it to ask a question.
This is called Relational Division.
SELECT produktID
FROM tableName
WHERE ingredientID IN (1,5,6)
GROUP BY produktID
HAVING COUNT(*) = 3
SQLFiddle Demo
If a unique constraint was not enforce on ingredientID for every produktID, then you need to use DISTINCT
SELECT produktID
FROM tableName
WHERE ingredientID IN (1,5,6)
GROUP BY produktID
HAVING COUNT(DISTINCT ingredientID) = 3
SQLFiddle Demo
Other Source
Relational Division
Try this:
SELECT pi.productID, p.productName
FROM products_ingredients_mm pim
INNER JOIN products p ON pim.productID = p.productID
WHERE ingredientID IN (1,5,6)
GROUP BY productID
HAVING COUNT(DISTINCT ingredientID) = 3