php mysql get all parents - php

i'm trying to get all parents of children. I have a simple query
select from sections where sectionID = 6
The database structure is very simple
sectionID parent
1 0
2 1
3 1
4 2
5 4
6 5
Now I'm trying to get all parents from sectionID 6, the result should be a string 0/2/4/5/6.
children might not have a lot of parents, it might have only one so the result should be different. i.e
5/6.. I hope you understand what I'm trying to do. I have tried couple recursive functions which i've found on the internet, but I really suck at this and was wondering if anyone could help me to get on the right track. P.S I'm using php and mysql

Well, as far as I can see you have two choices, both well known.
1) You make a recursive function, just the ones you have been trying. There are tons of them around and i won't put him in here.
2) By far my favourite, this is the Database pattern most modern ORM use, it's called nested set model.
Basically you create a few more columns on the table, it should look like this one:
CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
INSERT INTO nested_category VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);
SELECT * FROM nested_category ORDER BY category_id;
+-------------+----------------------+-----+-----+
| category_id | name | lft | rgt |
+-------------+----------------------+-----+-----+
| 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 |
| 3 | TUBE | 3 | 4 |
| 4 | LCD | 5 | 6 |
| 5 | PLASMA | 7 | 8 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 7 | MP3 PLAYERS | 11 | 14 |
| 8 | FLASH | 12 | 13 |
| 9 | CD PLAYERS | 15 | 16 |
| 10 | 2 WAY RADIOS | 17 | 18 |
+-------------+----------------------+-----+-----+
If you notice there is no parent_id column. To be able to search for it's childrens for, let's say row 5 the query will be like:
Select * from nested_category where left > 7 and left < 8 order by left asc, which will bring no results.
For row number 1 the result however will bring the entire tree.
I have no php script for autocreating those columns on this computer, but there are plenty around. Im afraid they are also recursive.
You my find lots of info around searching for "nested set model", like this or theorical exaplanations of the model like this one
AND THIS IS A WAY DUPLICATED QUESTION (i can not put it as duplicated)
Some other answers:
This guys do it all in mysql
Some more answers here
You should reread the forum rules before posting, look for already asked questions.
Hope it helps.

I'm not going to write all the code, but this idea should work if I understand your database shema correctly.
section = 6
result = ""
while section != ""
select parent where sectionID = section
result += parent
section = parent
return result

Related

Can SELECT, SELECT COUNT and cross reference tables be handled by just one query?

I have a page that displays a list of projects. With each project is displayed the following data retrieved from a mysqli database:
Title
Subtitle
Description
Part number (1 of x)
The total number of photos associated with that project
A randomly selected photo from the project
A list of tags
Projects are displayed 6 per page using a pagination system
As this is based on an old project of mine, it was originally done with sloppy code (I was just learning and did not know any better) using many queries. Three, in fact, just for items 5-7, and those were contained within a while loop that worked with the pagination system. I'm now quite aware that this is not even close to being the right way to do business.
I am familiar with INNER JOIN and the use of subqueries, but I'm concerned that I may not be able to get all of this data using just one select query for the following reasons:
Items 1-4 are easy enough with a basic SELECT query, BUT...
Item 5 needs a SELECT COUNT AND...
Item 6 needs a basic SELECT query with an ORDER by RAND LIMIT 1 to
select one random photo out of all those associated with each project
(using FilesystemIterator is out of the question, because the photos
table has a column indicating 0 if a photo is inactive and 1 if it is
active)
Item 7 is selected from a cross reference table for the tags and
projects and a table containing the tag ID and names
Given that, I'm not certain if all this can (r even should for that matter) be done with just one query or if it will need more than one query. I have read repeatedly how it is worth a swat on the nose with a newspaper to nest one or more queries inside a while loop. I've even read that multiple queries is, in general, a bad idea.
So I'm stuck. I realize this is likely to sound too general, but I don't have any code that works, just the old code that uses 4 queries to do the job, 3 of which are nested in a while loop.
Database structure below.
Projects table:
+-------------+---------+----------+---------------+------+
| project_id | title | subtitle | description | part |
|---------------------------------------------------------|
| 1 | Chevy | Engine | Modify | 1 |
| 2 | Ford | Trans | Rebuild | 1 |
| 3 | Mopar | Diff | Swap | 1 |
+-------------+---------+----------+---------------+------+
Photos table:
+----------+------------+--------+
| photo_id | project_id | active |
|--------------------------------|
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
| 5 | 2 | 1 |
| 6 | 2 | 1 |
| 7 | 3 | 1 |
| 8 | 3 | 1 |
| 9 | 3 | 1 |
+----------+------------+--------+
Tags table:
+--------+------------------+
| tag_id | tag |
|---------------------------|
| 1 | classic |
| 2 | new car |
| 3 | truck |
| 4 | performance |
| 5 | easy |
| 6 | difficult |
| 7 | hard |
| 8 | oem |
| 9 | aftermarket |
+--------+------------------+
Tag/Project cross-reference table:
+------------+-----------+
| project_id | tag_id |
|------------------------|
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 2 |
| 2 | 5 |
| 3 | 6 |
| 3 | 9 |
+------------+-----------+
I'm not asking for the code to be written for me, but if what I'm asking makes sense, I'd sincerely appreciate a shove in the right direction. Often times I struggle with both the PHP and MySQLi manuals online, so if there's any way to break this down, then fantastic.
Thank you all so much.
You're able to do subqueries inside your SELECT clause, like this:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo
FROM projects as p
Now, for the list of tags, as it returns more than one row, you can't do a subquery and you should do one query for every project. Well, in fact you can if you return all the tags in some kind of concatenation, like a comma separated list: tag1,tag2,tag3... but I don't recommend this one time that you will need to explode the column value. Do it only if you have many many projects and the performance to retrieve the list of tags for each individual project is fairly low. If you really want, you can:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo,
(SELECT GROUP_CONCAT(tag SEPARATOR ', ') FROM tags WHERE tag_id in (SELECT tag_id FROM tagproject WHERE project_id = p.project_id)) as tags
FROM projects as p
As you said from item 1 to 4 you already have the solution.
Add to the same query a SQL_CALC_FOUND_ROWS instead of a SELECT COUNT to solve the item 5.
For the item 6 you can use a subquery or maybe a LEFT JOIN limiting to one result.
For the latest item you can also use a subquery joining all the tags in a single result (separated by comma for instance).

Dynamically create a menu from exploded datas from mysql database

I struggle to find an easy way to recreate a navigation structure with datas stored in a mysql database. I can't change the database structure, it's automatically generated.
I use Laravel 5.2 as the base framework for this project.
Here's the database structure:
Datas:
| id | origin | type | value |
|----|--------|------------|------------------------------------|
| 1 | 20 | menuLevel | 0 |
| 2 | 20 | menuTitle | The title of this menu item |
| 3 | 20 | menuParent | 0 |
| 4 | 20 | menuUrl | /url-of-the-menu |
| 5 | 21 | menuLevel | 1 |
| 6 | 21 | menuTitle | The title of this second menu item |
| 7 | 21 | menuParent | 20 |
| 8 | 21 | menuUrl | /url-of-the-submenu |
And much more data of this kind. But all menu items always contains four properties, the level, the title, the url and the parent item that I have to put together.
With this structure, I try to recreate the navigation but I can't find the proper way to query the database to obtain a result that I can then use with a jquery tree plugin to navigate through the generated menu.
Ideally, this structure would be:
{
'origin': {
'menuLevel': 0,
'menuTitle': 'The title',
'menuParent': 0,
'menuUrl': '/url'
}, and so on..
}
Does anyone already faced this kind of structure? What could be the best way to achieve this? Thanks in advance for pointing me in the right direction.
if by 'origin' you mean 20, you probably could query
SELECT * FROM [tablename] ORDER BY origin
and then put them into an array until the origin changes, then open a new array. This also fetches the unstructured data.
if you want exactly those four attributes, you could do a query like
SELECT t1.origin, t1.value as menuLevel, t2.value as menuTitle,
t3.value as menuParent, t4.value as menuUrl
FROM [table] t1
LEFT JOIN [table] t2 ON (t1.origin=t2.origin AND t2.type='menuTitle')
LEFT JOIN [table] t3 ON (t1.origin=t3.origin AND t3.type='menuParent')
LEFT JOIN [table] t4 ON (t1.origin=t4.origin AND t4.type='menuUrl')
WHERE t1.type='menuLevel'
This could be extended to all the unstructured data as well, you'd then have a lot of NULLs that you could filter out or not.
add: I hope laravel puts an index on (id, type) or this query might be quite slow.

MySQL mylty sorting a table

I have a MySQL table users:
# | name | parent|
________________________
1 | USER 1 |
2 | USER 2 |
3 | user 12 | 1
4 | user 22 | 2
5 | user 11 | 1
6 | USER 3 |
7 | user 21 | 2
8 | user 31 | 6
Here the parent record is the primary key of the same table. What i need is to sort the table both parent-wise and name-wise.
This is the result that I need to get:
# | name | parent|
________________________
1 | USER 1 |
5 | user 11 | 1
3 | user 12 | 1
2 | USER 2 |
7 | user 21 | 2
4 | user 22 | 2
6 | USER 3 |
8 | user 31 | 6
If you only have one level of parents you can do:
select u.*
from users u
order by coalesce(parent, #), #;
(This assumes that the id of the parent is smaller than the id of the children, as is the case with the sample data in the question.)
If you have multiple levels of parents (grandparents and so on), then a single MySQL query is problematic. MySQL doesn't directly support hierarchical or recursive queries.
Although I do not know your use case, I think what you are doing is a symptom of poor design.
If possible, a rethink might be in order.
Nevertheless, I think this works
SELECT * FROM users
ORDER BY
CASE `parent`
WHEN NOT NULL THEN `parent`
ELSE SUBSTRING_INDEX(`users`.`name`, ' ', -1) END
ASC
Simple explanation:
Sorts by parent field if not null.
Otherwise use the number found in the name field (extracted by separating by the space).
Assumes number will always be separated by a space, and as with Gordon's answer, only works with a single parent.

How to write a MYSQL query that will return children nested under parents?

I don't know if what I'm asking is even possible, but here's my situation. I have a table structured somewhat like this:
+--------------------------------------------------+
| id | parent_id | name | category | ....
+--------------------------------------------------+
| 0 | -1 | item0 | 1 |
| 1 | 0 | item1 | 1 |
| 2 | 0 | item2 | 1 |
| 3 | 2 | item3 | 1 |
| 4 | 2 | item4 | 1 |
| 5 | -1 | item5 | 1 |
+--------------------------------------------------+
A parent_id of -1 will mean it is a "base" item with no parent. Each item will have more columns of information. I need to somehow output all items in a category nested like the following:
item0 => item1
=> item2
=> item3
=> item4
item5
I don't know if that makes sense or not, but hopefully it does!
The only way I can think of doing this is making a query to get all of the "base" items (querying for rows with parent_id = -1) then iterate through every resulting row, querying for rows that have their parent_id equal to the current row's id, then repeating the process going deeper and deeper until there aren't any more children for a base item.
Is there a better way?
Thanks!!
It is not possible in pure SQL.
SQL is intended to work with relational data not trees (hierarchical data).
You can represent tree in an SQL schema, however you won't be a able to result in a tree as you intend to do.
The only way to do is to get an usable result by making as many join as level you're storing.
Your current schema may support multiple level, however, it will be very difficult to manage more than one or two level.
You may be interested in Nested Set Model or Managing hierarchical data in mysql
There are some implementation of the Nested Set like this one to work with Doctrine 2
This is not possible in pure SQL and it is one of the aspects of the relational model that generates most criticism.
I would recommend you to read the links on this post: SQL "tree-like" query - most parent group
And also, if your application relies too much on this, I would suggest you to take a look at some non-relational databases that can represent this kind of data way better, such as MongoDB ( www.mongodb.org )
I hope i understood well your question(it's pretty late here and i've just come from a bar), if i didnt, just correct me and i'll rewrite my answer.
From the scenario given, i guess there's another parent table, isn't there?
Lets imagine it's attributes are id and name. Children table is the given one by you (w/o unnecessary attributes).
mysql> insert into parent(name) values ('petr'),('tomas'),('richard');
mysql> insert into children(name,parent_id) values('michal',1),('tomas',1),('michal');
mysql> select parent.id,parent.name,children.name from parent left join children on parent.id = children.parent_id;
+----+---------+--------+
| id | name | name |
+----+---------+--------+
| 1 | petr | michal |
| 1 | petr | tomas |
| 2 | tomas | NULL |
| 3 | richard | michal |
+----+---------+--------+
In order to do this multiple time (parent got child who got child who got child etc..) You can accomplish that by using multiple joins.
mysql> select parent.id,parent.name as Parent,children.name as Child,children2.name as Child2 from parent left join children on parent.id = children.parent_id left join children2 on children.id = children2.parent_id;
+----+---------+--------+--------+
| id | Parent | Child | Child2 |
+----+---------+--------+--------+
| 1 | petr | michal | NULL |
| 1 | petr | tomas | dan |
| 1 | petr | tomas | pavel |
| 2 | tomas | NULL | NULL |
| 3 | richard | michal | michal |
+----+---------+--------+--------+
If i either didnt answer what you asked or you need further explanation let me know ;]
Regards,
Releis

What is the algorithm behind nested comments?

I want to learn the comment displaying algorithm behind Reddit. How is a comment related with its child and so on? How they are stored in the database?
Lets say
comment1
-comment2
--comment3
-comment4
--comment5
--comment6
---comment7
----comment8
comment9
How to display comment5 which is after comment4 which is after comment1? What is the idea behind this sequencing? And how to relate them in the database?
It is called hierarchy. Each comment either has no parent comment, or has one parent comment. This way you can display every "top level" comment (thanks to the fact they have no parent comments), then child comments for each of them etc. etc.
And the database structure may look like this for comments table:
id field identifying single comment,
parent_id being set to parent's ID or not set (set to NULL or set to 0),
created - timestamp for comment creation,
content - actual comment content,
any additional field you need,
AS #Rafe said, the actual storage is pretty easy, it would be something like:
| id | name | parent |
| 1 | comment1 | 0 |
| 2 | comment2 | 1 |
| 3 | comment3 | 2 |
| 4 | comment4 | 1 |
| 5 | comment5 | 4 |
| 6 | comment6 | 4 |
| 7 | comment7 | 6 |
| 8 | comment8 | 7 |
| 9 | comment9 | 0 |
Of course actually getting information from this is (arguably) the hard part. You can of course get the children of a comment with something like: SELECT * FROM table WHERE parent='4' will give you all the children of comment4. But counting children, listing all the children in hierarchical order would be a bit harder. Other answers may provide more information on that.
Pretty much what #Rafe Kettler noted - comments can have parent columns. However, if you want a more detailed and in-depth algorithm to use as a pattern for your implementation, take a look at this message threading algorithm.

Categories