I have a table where i store categories with its parent's id. Following is the table structure
What i need is to get the parent and details of id 4 in a single query without php recursion
id | CategoryName | parentid
1 Web 0
2 Software 0
3 PHP 1
4 Arrays 3
A self join should be sufficient here. Your goal is to join the table against itself, while relating the parentid of the main row to the id of its parent's row.
SELECT
me.id AS me_id,
me.CategoryName AS me_category,
parent.id AS parent_id,
parent.CategoryName AS parent_category
FROM
tablename me JOIN tablename parent ON me.parentid = parent.id
WHERE me.id = 4
Would a variation of
select * from categories t1 join categories t2 where t1.parentid=t2.id
work for you
Related
SELECT * FROM categories WHERE status = 1
I would like to get results from categories if the "services" table has any data for that specific category id. I was thinking about Left join or something like that. Any suggestions? (I use PHP btw)
You can use EXISTS:
SELECT c.*
FROM categories AS c
WHERE c.status = 1 AND EXISTS (SELECT 1
FROM services AS s
WHERE s.category_id = c.id)
I have two table suppose products and auto_assign_prod_list. I want to populate a dropdown list with the id of products table that are not present in auto_assign_prod_list table.
Suppose,
product table contain
Id
------
1
2
3
4
5
auto_assign_prod_list table contain
Id
-----
1
5
So, my result set will be
2
3
4
How is it possible using MySQL and PHP ?
Try this:
SELECT Id FROM product
WHERE Id NOT IN (SELECT Id FROM auto_assign_prod_list)
It will select the ids from product table which are not in auto_assign_prod_list table.
Result:
Id
------
2
3
4
See result in SQL Fiddle.
use a left join
select p.id
from products p
left join auto_assign_prod_list a on a.id = p.id
where a.id is null
SQLFiddle demo
See this great explanation of joins
SELECT
p.product,
q.format,
p.title
FROM
product p
JOIN info q ON p.product = q.product
WHERE p.user='$user'
GROUP BY p.product,q.format
I want to first group by 'product' from the product table but the also by format on the info table.
This is to not show duplicates of format and product. At the moment only the grouping by product is working.
Table - products
product | title
0 one
1 two
1 two - a
2 three
Table - product_details
product | title | format |
0 one home
1 two home
1 two - a home
2 three work
So for this example I want a list like:
product | title | format
0 one home
2 three work
Instead of:
product | title | format
0 one home
1 two home
2 three work
After your table structures were posted, I can see what your intent is, I believe. It looks like you are attempting to limit your output result set to those values for product.product which are never repeated. That is, values for product.product which have exactly one product.title.
For that, you can use a GROUP BY aggregation to return only those with COUNT(*) = 1 after the group is applied.
In this case, since you only expect one row back per product.product anyway, you can do the aggregation at the top level, not requiring a subquery. If you had joined in other tables, and ended up getting multiple rows back per product due to other one-to-many relationships, you would need to use the subquery method instead (to be portable anyway - MySQL would still probably allow this)
SELECT
p.product,
q.format,
p.title
FROM
products p
JOIN product_details q ON p.product = q.product
GROUP BY
p.product,
q.format,
p.title
HAVING COUNT(*) = 1
Here is a demonstration: http://sqlfiddle.com/#!2/72eda/6
If you did expect multiple rows back per p.product, such as if you joined in additional one-to-many related tables, an efficient way to handle that is to perform a JOIN against a subquery that imposes that limit in the HAVING clause. Those which don't meet the HAVING condition won't be returned in the subquery and therefore get discarded by the INNER JOIN.
SELECT
p.product,
q.format,
p.title
FROM
products p
INNER JOIN product_details q ON p.product = q.product
/* Subquery returns only product values having exactly 1 row */
INNER JOIN (
SELECT product
FROM products
GROUP BY product
HAVING COUNT(*) = 1
) pcount ON p.product = pcount.product
WHERE p.user = '$user'
http://sqlfiddle.com/#!2/72eda/2
I would like to create a pretty url menu array from my nested set list of categories.
The tables:
categories (lft, rgt, id)
categories_description (cat_id, lang_id, name)
seo_url (cat_id, prod_id, man_id, url)
Table categories holds all categories, category names come from table categories_description and pretty urls come from table seo_url.
Is there a way to combine all three tables and fetch the whole menu array in 1 query?
For example:
parent (with name of parent)
parent/subcat (with name of subcat)
parent/subcat2 (with name of subcat2)
parent2 (with name of parent2)
parent2/subcat32 (with name of subcat32)
parent2/subcat42/subcat23 (with name of subcat23)
parent3 (with name of parent3)
parent4 (with name of parent4)
parent4/subcat4 (with name of subcat4)
if they are only nested to a set depth (eg 3 levels max as per your example), then you should be able to. you would end up with some columns being null, which you would have to cater for in code, rather then the sql query.
it is hard to give you a concrete example with the data you have provided
but it wuld be something like this (note this is untested)
select parent_description.name as parent_name,
parent_url.url as parent_url,
child_description.name as child_name,
child_seo_url.url as child_url,
grandchild_description.name as grandchild_name,
grandchild_seo_url.url as grandchild_url,
from categories as parent
join category_description as parent_description on parent.id=parent_description.cat_id
join seo_url as parent_seo_url on parent.id=parent_seo_url.cat_id
left outer join categories as child on parent.id=child.parent_id
left outer join category_description as child_description on child.id=child_description.cat_id
left outer join seo_url as child_seo_url on child.id=child_seo_url.cat_id
left outer join categories as grandchild on grandchild.id=child.parent_id
left outer join category_description as grandchild_description on grandchild.id=grandchild_description.cat_id
join seo_url as grandchild_seo_url on grandchild.id=grandchild_seo_url.cat_id
which should give an out put like
parent_name | parent_url | child_name | child_url | grandchild_name | grandchild_url
parent | url | NULL | NULL | NULL | NULL
parent | url | child | url | NULL | NULL
parent | url | child | url | grandchild | url
you should be able to render the html from that
You could simply fetch all categories at once and build the hierarchy in code.
select *
from categories c
join categories_description d on d.cat_id = c.id
join seo_url u on d.cat_id = u.id;
I'm not very fluent in PHP anymore, but using a hash to lookup parent categories should work pretty well. Finally, you'll have to sort the whole list according to your needs.
Performance wise, I wouldn't worry too much. Building and sorting the menu should still be faster than the data fetching itself (given that the code is well written) as we're talking about a few ms here. The whole structure would certainly be a good candidate for caching though - but don't do it before you have to.
Thanks for the solutions. I've decided to use this query because it exports the menu array in the same way as I wrote in my question.
SELECT node.cat_id, node.lft, node.rgt, GROUP_CONCAT(su.url ORDER BY parent.lft SEPARATOR "/" ) AS path, (COUNT(parent.lft) - 1) AS depth, cd.name AS name
FROM categories AS node
JOIN categories AS parent ON node.lft BETWEEN parent.lft AND parent.rgt
JOIN seo_url AS su ON parent.cat_id = su.cat_id
JOIN categories_description AS cd ON node.cat_id = cd.cat_id
WHERE parent.lft >= 1
GROUP BY node.cat_id
ORDER BY node.lft
I have a table for posts and a table for categories, what i want is to select posts that are in a specific category. The problem is that the category is stored in another table and not in the posts table, here is the example:
posts
id title body
---------------------------
125 Some title Blah blah
categories
postid category
----------------
125 politic
I want in single query to fetch posts in the politic category by example, what to do?
Use:
SELECT p.id,
p.title,
p.body
FROM POSTS p
JOIN CATEGORIES c ON c.postid = p.id
WHERE c.category = 'politic'
The issue I have with your CATEGORIES table is that storing the category value as a string means the data isn't normalized - you should instead have a CATEGORY table:
CATEGORY
category_id (primary key, auto_increment)
category_description
...and use the category_id value in the CATEGORIES table:
CATEGORIES
category_id (primary key, foreign key to CATEGORY.category_id)
post_id (primary key, foreign key to POSTS.postid)
select p.*
from posts p
inner join categories c
on p.id = c.postid
where
c.category = 'politic'
You need to join the two tables in your query
SELECT *
FROM posts P
INNER JOIN categories C on C.post_id = P.id
WHERE C.category = 'politic'
You can intuitively (this is NOT exactly a technically correct explanation) think of this join as appending the category field to a row in posts with the shared id (this is the 'on C.post_id = P.id'). The WHERE clause indicates that you want only those rows where category is 'politic'.
Inner join is one of several join types. Left join in particularly is another common one, and its difference in this situation is that rows of post without a match in categories would still be listed, but with nulls for the fields from categories. (With inner join such rows would not remain)
http://en.wikipedia.org/wiki/Join_%28SQL%29