MySQL: Selecting an entry's parent and grandparent names - mysql

On my site, I have several articles, and each article is assigned to a location with a category. This is how it has been set up for quite some time, so I can't change how it works.
The relationship between these is set in two tables, categories and category_posts:
categories
+--------+------------------+------------------+-----------+
| cat_id | cat_name | cat_url_title | parent_id |
+--------+------------------+------------------+-----------+
| 1 | Toronto | toronto | 2 |
| 2 | Ontario | ontario | 3 |
| 3 | Canada | canada | 0 |
| 4 | Vancouver | vancouver | 5 |
| 5 | British Columbia | british_columbia | 3 |
| 6 | Montreal | montreal | 7 |
| 7 | Quebec | quebec | 3 |
| 8 | San Francisco | san_francisco | 9 |
| 9 | California | california | 10 |
| 10 | United States | united_states | 0 |
+--------+------------------+------------------+-----------+
category_posts
+----------+--------+
| entry_id | cat_id |
+----------+--------+
| 990 | 1 |
| 991 | 4 |
| 992 | 8 |
| 993 | 6 |
| 994 | 4 |
| 995 | 4 |
| 996 | 1 |
| 997 | 6 |
| 998 | 8 |
| 999 | 4 |
| 1000 | 3 |
| 10001 | 10 |
+----------+--------+
So category posts contains a list of all the articles' entry id's, and which category they have been assigned. Then the categories table lists all the categories, including their parents and their URL titles.
What I'm trying to do is write a query that for any article entry id, returns its category name and url title, as well of those of its parents and grandparents.
So for example, if I used 999 as the entry id, this is the result I would want:
+-----------+---------------+------------------+------------------+------------------+-----------------------+
| cat_name | cat_url_title | parent_name | parent_url_title | grandparent_name | grandparent_url_title |
+-----------+---------------+------------------+------------------+------------------+-----------------------+
| Vancouver | vancouver | British Columbia | british_columbia | Canada | canada |
+-----------+---------------+------------------+------------------+------------------+-----------------------+
Now, I'm kind of stuck with writing a query to do that. I know how to get just the entry's own category name and url title using this query:
SELECT cat_name, cat_url_title FROM categories
JOIN category_posts
ON categories.cat_id=category_posts.cat_id
WHERE category_posts.entry_id = 999;
But how would I include the parent and grandparent categories? Hopefully that makes sense.
EDIT: I just realized a modification is necessary. I can't be certain that only child categories will be selected as the category for every article. Parent and even grandparent categories could also be selected. When I put the such an article's entry id into the query, it returns with no results. I updated the category_posts table to show this. Does anyone know how I can do this?

you can join three instances of your categories table to get that:
SELECT
child.cat_name as child_cat_name, child.cat_url_title as child_cat_url_title,
parent.cat_name as parent_cat_name, parent.cat_url_title as parent_cat_url_title,
grandparent.cat_name as grandparent_cat_name, grandparent.cat_url_title as grandparent_cat_url_title
FROM categories as child
JOIN category_posts ON child.cat_id=category_posts.cat_id
JOIN categories as parent on child.parent_id = parent.cat_id
JOIN categories as grandparent on parent.parent_id = grandparent.cat_id
WHERE category_posts.entry_id = 999;

SELECT
c1.cat_name,
c1.cat_url_title,
c2.cat_name AS parent_name,
c2.cat_url AS parent_url_title,
c3.cat_name AS grandparent_name,
c3.cat_url AS grandparent_url_title
FROM categories c1
INNER JOIN categories c2 ON (c1.parent_id = c2.cat_id)
INNER JOIN categories c3 ON (c2.parent_id = c3.cat_id)
INNER JOIN category_posts p ON (c1.cat_id = p.cat_id)
WHERE p.entry_id = 999;

Please try this :
select c.cat_name,c.cat_url_title,
b.cat_name as cat_name_parent,b.cat_url_title as cat_url_title_parent,
a.cat_name as cat_name_grandparent,a.cat_url_title as cat_url_title_grandparent
from categories a
inner join categories b on b.parent_id = a.cat_id
inner join categories c on c.parent_id = b.cat_id
inner join category_posts d on c.cat_id = d.cat_id and d.entry_id = '999'

Related

I want to select data in 1 table

structure table
Pk id <------------| primary Key
name |
country |
fk parent_id <-----| forigen key
Data
id| name | country | parent_id
1 | Diva | Portugal | 2
2 | Alex | Georgia | 2
3 | Bianca | Palau | 4
4 | Tony | Montenegro | 1
result
id| name | country | parent_id | name_parent_id |
1 | Diva | Portugal | 2 | Alex |
2 | Alex | Georgia | 2 | Alex |
3 | Bianca | Palau | 4 | Tony |
4 | Tony | Montenegro | 1 | Diva |
the result for this case
Any advice on this one?
You can use the same table twice in order to select the parent name:
SELECT
c.id,
c.name,
c.country,
c.parent_id,
p.name AS name_parent_id
FROM
YourTable AS c
INNER JOIN YourTable AS p ON c.id = p.parent_id
I used c as alias for the child and p for the parent.
Note: You can use LEFT JOIN instead of INNER JOIN in case of the existence of the parent is not required.

school work delivery list mysql

I have two tables:
Table students and table of school work delivered
Students table
+--------------------------+---------------------------------+
| id | name |
+--------------------------+---------------------------------+
| 1 | ADAM |
| 2 | BRIGITTE |
| 3 | ANNE |
+--------------------------+---------------------------------+
table student works
+---------------+-------------------------+------------------+
| id_works | works | id_student |
+---------------+-------------------------+------------------+
| 1 | airplane wing | 1 |
| 2 | volcano | 2 |
| 3 | law of gravity | 1 |
| 4 | airplane wing | 3 |
| 5 | law of gravity | 1 |
+-----------------------------------------+------------------+
How do I make a SELECT for work that returns the entire list of students, indicating that the work is delivered? (IMPORTANT: list of all students)
Example
LIST FOR WORK **airplane wing**
+--------------------------+---------------------------------+
| ADAM | X |
| BRIGITTE | |
| ANNE | X |
+--------------------------+--------------------- -----------+
I have tried it with LEF JOIN and IF, but it is not the list of all the students without repeating them.
SELECT
s.name ,
w.work,
w.resid_id,
if(w.work = 'airplane wing', 'X', '') as mark
FROM students s
LEFT JOIN works w
ON s.id = w.id_student
ORDER BY s.name ASC
This will give you a list of all students
And fields id_works and works will be null for those who didn't complete the work
SELECT s.name, w.id_works, w.works
FROM students s
LEFT JOIN works w
ON (w.id_student = s.id AND w.works = 'airplane wing')
ORDER BY s.name ASC

how to get count of records from 2 tables based on value in a particular column from 1st table using MySQL

These are the tables I am fetching count from
register
+----+-------------+--------+
| id | empSignupId | cityId |
+----+-------------+--------+
| 42 | 4 | 1 |
| 47 | 3 | 1 |
| 48 | 11 | 1 |
| 54 | 20 | 1 |
| 55 | 21 | 2 |
| 56 | 22 | 2 |
+----+-------------+--------+
guest_list
+-----+------------+-------------+
| id | guestName | empSignupId |
+-----+------------+-------------+
| 103 | Mallica SS | 3 |
| 104 | Kavya | 3 |
| 108 | Vinay BR | 11 |
| 109 | Akash MS | 11 |
+-----+------------+-------------+
cities
+----+---------------+
| id | cityName |
+----+---------------+
| 1 | Bengaluru |
| 2 | Chennai |
| 3 | Sydney |
| 4 | New York City |
| 5 | Shanghai |
| 6 | Chicago |
+----+---------------+
I need to fetch the count of people registered from particular city which includes people, their guests, if guests are not present also it should show the count of people.
This is what I tried
SELECT COUNT(gl.id) + COUNT(rfs.id), ct.cityName, rfs.cityId
FROM register rfs
INNER JOIN cities ct ON ct.id=rfs.cityId
INNER JOIN guest_list gl ON gl.empSignupId = rfs.empSignupId
GROUP BY rfs.cityId;
+-------------------------------+-----------+--------+
| COUNT(gl.id) + COUNT(rfs.id) | cityName | cityId |
+-------------------------------+-----------+--------+
| 8 | Bengaluru | 1 |
+-------------------------------+-----------+--------+
I also need the count of people from other cities to be displayed, since there are no guests from some cities its not returning that count.
Please help me figure this out, I am still new to MySQL.. any help is greatly appreciated.
You fist need to aggregate guest_list table in order to get the number of records per empSignupId:
SELECT empSignupId, COUNT(empSignupId) AS countGuest
FROM guest_list gl
GROUP BY empSignupId
Output:
empSignupId countGuest
----------------------
3 2
11 2
Now you have to use a LEFT JOIN to the derived table above in order to also get the number of records for each city:
SELECT COALESCE(SUM(countGuest), 0) + COUNT(rfs.id), ct.cityName, rfs.cityId
FROM register rfs
INNER JOIN cities ct ON ct.id=rfs.cityId
LEFT JOIN (
SELECT empSignupId, COUNT(empSignupId) AS countGuest
FROM guest_list gl
GROUP BY empSignupId
) gl ON gl.empSignupId = rfs.empSignupId
GROUP BY rfs.cityId;
Output:
COALESCE(SUM(countGuest), 0) + COUNT(rfs.id) cityName cityId
------------------------------------------------------------------
8 Bengaluru 1
2 Chennai 2
Using LEFT JOIN instead of INNER JOIN guarantees that we also get cities without guests.
Demo here
Note: If you also want to get cities without registrations then you need to place the cities table first and use a LEFT JOIN to register.
Use LEFT JOINS and add count(distinct r.empSignupId) + count(distinct g.id):
select
c.id as cityId,
c.cityName,
count(distinct r.empSignupId) + count(distinct g.id) as people_count
from cities c
left join register r on r.cityId = c.id
left join guest_list g on g.empSignupId = r.empSignupId
group by c.id;
The result would be:
| cityId | cityName | people_count |
|--------|---------------|--------------|
| 1 | Bengaluru | 8 |
| 2 | Chennai | 2 |
| 3 | Sydney | 0 |
| 4 | New York City | 0 |
| 5 | Shanghai | 0 |
| 6 | Chicago | 0 |
Demo: http://rextester.com/OTBH14189
If you don't need the rows with 0, change the first LEFT JOIN to an inner JOIN.

SQL NOT LIKE with JOIN

I'm using MYSQL and can't get a NOT LIKE working with multiple JOINS.
I have 3 tables which look like this:
Parents (Table1):
+------------+-------+-----+
| ParentID | Name | AGE |
+------------+-------+-----+
| 1 | Peter | 26 |
| 2 | Karl | 33 |
| 3 | Tessa | 43 |
+------------+-------+-----+
Kids (Table2):
+------------+-------+-----+
| KidID | Name | AGE |
+------------+-------+-----+
| 1 | Mike | 3 |
| 2 | Mike | 13 |
| 3 | Jenna | 4 |
| 4 | Jessi | 14 |
+------------+-------+-----+
Parents_Kids (Table3):
+-----------+-------+
| ParentID | KidID |
+-----------+-------+
| 1 | 2 |
| 1 | 4 |
| 2 | 1 |
| 3 | 3 |
+-----------+-------+
Now i want to get all parent names who don't have a kid named Mike or any form of Mike in there name.
I tried this:
SELECT p.name
FROM PARENTS p JOIN
Parents_Kids pk
ON pk.ParentID = p.ParentID JOIN
Kids k
ON k.KidID = pk.KidID
WHERE k.Name NOT LIKE '%mike%';
But the result is wrong with this query.
If i try this query with LIKE it works correctly but not with NOT LIKE.
I think you don't want to select the parent if atleast one of his/her kids have a name like mike. You can use having to filter such cases.
SELECT p.name
FROM PARENTS p
JOIN Parents_Kids pk ON pk.ParentID=p.ParentID
JOIN Kids k ON k.KidID=pk.KidID
group by p.name
having count(case when k.name like '%mike%' then 1 end) = 0
You say your query works correctly with LIKE, so a way to get the other parent names is this:
SELECT name
FROM parents
WHERE name not in (SELECT p.name
FROM PARENTS p JOIN
Parents_Kids pk
ON pk.ParentID = p.ParentID JOIN
Kids k
ON k.KidID = pk.KidID
WHERE k.Name LIKE '%mike%')

Multiple Select Join query with corresponding column on one table to others

Lets say that I have something table like this:
<b>Name</b>
+---------+--------+
| name_id | name |
+---------+--------+
| 5 | Betti |
| 6 | Derry |
| 7 | Alfred |
| 8 | Elsie |
| 9 | Cinta |
+---------+--------+
<b>Goods</b>
+----------+-----------+
| goods_id | goods |
+----------+-----------+
| 1 | Computer |
| 2 | AC |
| 3 | Microwave |
| 4 | TV |
+----------+-----------+
<b>Transaction</b>
+-------+---------+----------+
| ai_id | name_id | goods_id |
+-------+---------+----------+
| 1 | 7 | 2 |
| 2 | 5 | 4 |
| 3 | 9 | 3 |
+-------+---------+----------+
I want to replace name_id column on Transaction table with name column on Name table with corresponding name_id column and so for goods_id to produce something similar to this table:
<b>Transaction</b>
+-------+--------+-----------+
| ai_id | name | goods |
+-------+--------+-----------+
| 1 | Alfred | AC |
| 2 | Betti | TV |
| 3 | Cinta | Microwave |
+-------+--------+-----------+
If you're looking to just display the information rather than actually "replacing" your tables with new ones, then you could use a SELECT query with a simple INNER JOIN. This way you can display columns from multiple tables.
SELECT T.ai_id, N.Name, G.goods
FROM Transaction T
INNER JOIN Name N ON N.name_id = T.name_id
INNER JOIN Goods G ON G.goods_id = T.goods_id;
Try with inner join
SELECT T.ai_id,N.name,G.goods
FROM Transaction as T
INNER JOIN Goods as G ON T.goods_id = G.goods_id
INNER JOIN Name as N ON N.name_id = T.name_id;
Try this one
select tb3.ai_id,tb2.name,tb1.goods from Goods tb1,Name tb2,Transaction tb3 where tb3.name_id=tb2.name_id and tb3.goods_id=tb1.goods_id order by tb3.ai_id
try this:
SELECT C.ai_id,A.name,B.goods
FROM Transaction C
INNER JOIN Name A
ON A.name_id=C.name_id
INNER JOIN Goods B
ON B.goods_id=C.goods_id;
http://sqlfiddle.com/#!2/3c5f3/8