I got an existing products database which I'm writing an administration tool for (in PHP).
The database contains the following "categories" table:
Table Categories
--------------------
PK | id
FK | parent_id
| title
Now the foreign key "parent_id" contains an id taken from the same table, or "0" if it's a topmost category.
For creating an overview I now need a mysql statement which results in the following data:
id | parent_id | title | parent_title
The parent_title is where I've no idea. I created the following statement:
SELECT
c1.id,
c1.parent_id,
c1.title,
c2.title as `parent_title`
FROM
categories c1,
categories c2
WHERE
c1.parent_id = c2.id
I now only get all categories which have got a parent category.
Should be simple, and might have already been answered here. I think I only didn't find the right words to search for to find it by searching existing articles.
Thanks for your help,
Daniel
You can use a LEFT OUTER JOIN for this:
SELECT c1.id,
c1.parent_id,
c1.title,
c2.title as `parent_title`
FROM categories c1
left outer join categories c2 on c1.parent_id = c2.id
you're looking for an OUTER JOIN :)
See here: http://www.quackit.com/sql/tutorial/sql_outer_join.cfm
Related
I am trying to self join information from a table, to show a hierarchy (the table contains members, which consist of children and their parents). The table name is ‘members’, the four important columns are id, full_name, father_id and mother_id. The id's in the columns father_id and mother_id match the corresponding id's in column 'id'. As a result of the query, I would like to have a table with three columns, with column names 'Name', 'Father' and 'Mother'
I searched google, w3 schools and stack overflow, the closest to what I was looking for was a topic I found right here, called ‘Explanation of self-joins’ that had the following example code:
select
c1.Name , c2.Name As Boss
from
emp1 c1
inner join emp2 c2
on c1.Boss_id = c2.Id
I altered the query as follows (I didn't even dare include the third column yet :') ):
select
c1.full_name as Name, c2.full_name as Father
from
members1 c1
inner join members2 c2
on c1.father_id = c2.id
Unfortunately, I got an error message stating the following: 1146 - Table 'ppstb.members1' doesn't exist
I guessed it had something to do with how I simulated having two tables. The next thing I experimented with is:
select
c1.full_name , c2.full_name as Father
from
members c1, members c2
inner join members c2
on c1.father_id = c2.id
But this also gives an error: 1066 - Not unique table/alias: 'c2' What step am I missing?
last member should be c3 not c2
select
c1.full_name , c3.full_name as Father
from
members c1, members c2
inner join members c3
on c1.father_id = c3.id
I actually got the code working, using left-joins. I'm not sure why the inner joins wouldn't work, but this solved my problem:
SELECT
members.full_name, b.full_name AS Father, c.full_name AS Mother
FROM
members
LEFT JOIN
members b ON (members.father_id = b.id)
LEFT JOIN
members c ON (members.mother_id = c.id)
I have a table for categories. This has a recursive relationship so that a category can become a subcategory of another category. The table looks like this:
id name short_desc long_desc tag_id parent_id
I wrote simple to get sql to find all level 1 categories:
SELECT * FROM category WHERE parent_id =0
Then I wrote a query to get all of the level 2 categories (where parent category doesn't have a parent)
SELECT * FROM category WHERE parent_id IN (SELECT id FROM category WHERE parent_id =0)
What I would like to do, is produce a column where is shows all category data and any relevant parent category.
Logically like this:
select all from category
if parent_id != 0, add the parent as a new row
repeat 2 until all parents have been accounted for.
The result should look something like this:
id name short_desc long_desc tag_id parent_name parent_name_2
if the parent_name is null / empty, then parent_name should remain empty. if there is a parent_name id in the field, then check to see if there is a parent_name_2 and if so, populate both columns, if not then only populate parent_name.
I do have the option of coding this in jquery or php which I have a good idea how to do. However, I am sure that I can get the data I need from a good SQL query.
Any help would be greatly appreciated.
Kind Regards
Nick
Here's one option using multiple outer joins:
select c.*,
case when c2.id is not null then c2.name end parent_name,
case when c3.id is not null then c3.name end parent_name_2
from category c
left join category c2 on c.parent_id = c2.id
left join category c3 on c2.parent_id = c3.id
SQL Fiddle Demo
I have a mysql table named "category". the basic structure looks like this-
------ -------- ------
cat_id cat_name parent
------ -------- ------
1 test1 NULL
2 test2 NULL
3 test3 2
4 test4 1
5 test5 3
now i want all the category data with parent category name (not only id) in a single query. is that possible? i could do it by using a second query (getting child's parent name) in a while loop and merging data as a whole. but is it possible to do this with a single query?
Join the table with itself, using the parent column to link to the cat_id of the parent.
SELECT c1.cat_id as childID, c1.cat_name ChildName, c2.cat_name as ParentName
from category c1
LEFT OUTER JOIN category c2
ON c1.parent = c2.cat_id
Be careful: since some elements have no parents (NULL), I put a LEFT
OUTER JOIN so those rows are displayed as well. If you don't want
that, use a JOIN instead of LEFT OUTER JOIN.
You can also show the lines, but display something else (empty or a
text or ...) instead of the NULL by using COALESCE.
You can consider the result as one (big) new table, so you can add WHERE clauses as you usually do, for example filtering on the parent name: WHERE c2.cat_name = 'test2'
Select p.cat_id, p.cat_name, c.cat_id, c.cat_name, c.parent
From category Left Join category On p.cat_id = c.parent
Where p.cat_name = 'name'
SELECT c1.category_name AS category, c2. category_name AS sub_category
FROM (
SELECT *
FROM category
) AS c1
INNER JOIN (
SELECT *
FROM category
) AS c2 ON c1.category_id = c2.category_id
Hopefully the question explains it well. I have a DB for a Library. Since they can be used many times, and contains more data than just a name, I have a table for Authors. Then there's a table for Books. I have no problem linking Authors to Books via a column called Author_id.
What I'm trying to do is have a column called Author_IDs that contains a list of id's, since a book can have multiple IDs. In the Author_IDs column I have:
<id>3478</id>
<id>6456</id>
Using the ExtractValue function in MySQL I can link the table with one or the other id using:
WHERE Author.id = ExtractValue(Book.Author_IDs,"/id[2]") // to get the second ID.
My question is, I want to be able to automatically display all of the authors of a book, but don't know how to link to it more than once, without looping. How can I get the results to show me all of the authors?
(Or is there a better way to accomplish this?)
Firstly, I have to vote against your storage method. Storing data as xml inside a mysql column should be avoided if possible. If you use a normal approach you will find this problem to be much easier.
Create a table:
book_authors
book_id author_id
------- ---------
1 1
1 2
1 3
2 2
2 4
Then to get all of the authors associated with a certain book it's a simple query.
Select
b.book_id,
b.book_name,
GROUP_CONCAT(a.author_name) AS 'authors'
FROM
book_authors ba LEFT JOIN
books b ON ba.book_id = b.book_id LEFT JOIN
authors a ON ba.author_id = a.author_id
GROUP BY
ba.book_id
Not sure I understand completely. Could something like this do the trick?
select a.* from tblBooks b
left join tblAuthors a on (b.authors = concat('%', a.id, '%')
where b = 'book id';
I would have done it like this
Structure
tblBooks
--------
book_id
book_name
tblAuthors
----------
author_id
author_name
tblBooksToAuthors
-----------------
id
book_id
author_id
Query
select a.*
from tblAuthors a
left join tblBooksToAuthors b2a on (b2a.author_id = a.author_id)
where b2a.book_id = {your book id};
I have mysql tables defined as:
categories: category_id, category_name, parent_category_id
I'm looking for a nice sql query that would retrieve all the DESCENDANTS of a given category_id. That means, its children, and its children's children.
If that helps, we can assume a maximum number of levels (3). This query could be sent at any level (root, level 2, level 3).
Thanks!
Nathan
There are a few ways to store trees in a database. There's a fantastic article on sitepoint describing all the techniques:
http://articles.sitepoint.com/article/hierarchical-data-database/2
The one that is most appropriate if you want to be able to get an entire section of a tree in one query is Modified Preorder Tree Traversal.
This technique is also known as Nested Sets. There's more information here if you want more literature on the subject:
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
It can be done in a single query and a piece of recursive backend code logic: Formatting a multi-level menu using only one query.
If you also do PHP, this article comes with a PHP example as bonus, but translating to another language isn't that hard. I can't give any hints about that as you didn't mention the server side language you're using.
Hope this helps.
If you want to use this structure with max 3 levels you can join the table to itself three times:
SELECT
c1.id AS level_1,
c2.id AS level_2,
c3.id AS level_3
FROM categories c1
LEFT JOIN categories c2 ON c1.id = c2.parent_id
LEFT JOIN categories c3 ON c2.id = c3.parent_id
WHERE c1.parent_id IS NULL
I assume, that parent categories have NULL in parent_id.
Some example:
DECLARE #categories TABLE
(
id INT,
parent_id INT
)
INSERT INTO #categories(id,parent_id) VALUES(1,NULL)
INSERT INTO #categories(id,parent_id) VALUES(4,1)
INSERT INTO #categories(id,parent_id) VALUES(5,1)
INSERT INTO #categories(id,parent_id) VALUES(6,5)
INSERT INTO #categories(id,parent_id) VALUES(2,NULL)
SELECT * FROM #categories
SELECT c1.id AS level_1, c2.id AS level_2,
c3.id AS level_3
FROM #categories c1
LEFT JOIN #categories c2 ON c1.id = c2.parent_id
LEFT JOIN #categories c3 ON c2.id = c3.parent_id
WHERE c1.parent_id IS NULL
Returns:
level_1 | level_2 | level_3
---------------------------
1 | 4 | NULL
1 | 5 | 6
2 | NULL | NULL
MySQL does not support recursive queries, but you can find all descendants using a stored procedure with a WHILE loop. See the The edge list section of this book sample.