Left join select using Propel ORM - mysql

I have 3 table
major table:
+----+------------+
| id | major |
+----+------------+
| 1 | Computer |
| 2 | Architect |
| 3 | Designer |
+----+------------+
classroom table:
+----+----------+-------+
| id | major_id | name |
+----+----------+-------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 2 | A |
| 5 | 2 | B |
| 6 | 3 | A |
+----+----------+-------+
and finally, student_classroom table
+----+------------+--------------+----------+
| id | student | classroom_id | status |
+----+------------+--------------+----------+
| 1 | John | 1 | Inactive |
| 2 | Defou | 2 | Active |
| 3 | John | 2 | Active |
| 4 | Alexa | 1 | Active |
| 5 | Nina | 1 | Active |
+----+------------+--------------+----------+
how can I use propel to build query below
select
a.id,
a.major,
b.number_of_student,
c.number_of_classroom
from major a
left join (
select
major.major_id,
count(student_classroom.id) as number_of_student
from major
left join classroom on classroom.major_id = major.id
left join student_classroom on student_classroom.classroom_id = classroom.id
where student_classroom.`status` = 'Active'
group by major_id
) b on b.major_id = a.major_id
left join (
select
major.major_id,
count(classroom.id) as number_of_classroom
from major
left join classroom on classroom.major_id = major.id
group by major_id
) c on c.major_id = a.major_id
Because I want the final result would be something like this, I spend hours trying to figure it out without success.
+----+------------+-------------------+---------------------+
| id | major | number_of_student | number_of_classroom |
+----+------------+-------------------+---------------------+
| 1 | Computer | 4 | 3 |
| 2 | Architect | 0 | 2 |
| 3 | Designer | 0 | 1 |
+----+------------+-------------------+---------------------+

Try this
select
m.id,
m.major,
count(distinct s.id) as number_of_student ,
count(distinct c.id) as number_of_classroom
from major m
left join classroom c on
(m.id = c.major_id)
left join student_classroom s
on (s.classroom_id = c.id and c.major_id = m.id and s.status = 'active')
group by m.id
order by m.id

Related

Select rows that has common relationship

I have the following table:
Book
| id | book_title |
|----|------------|
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
Teacher:
| id | teacher_name |
|----|--------------|
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
Education stage:
| id | education_stage_name |
|----|----------------------|
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
The relationship between book and education stage:
| book_id | education_stage_id |
|---------|--------------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
And the relationship between book and teacher:
| book_id | teacher_id |
|---------|------------|
| 1 | 1 |
| 1 | 4 |
| 2 | 3 |
| 3 | 4 |
Now I want to select books, which has the same education stages as a teacher with id=1
So the result table should look like this:
| book_id | title |
|---------|-------|
| 1 | a |
Because only the education stage with id=1 is assigned to the book and the teacher with id=1 at the same time.
Here is the fiddle, but I don't know even how to start with this query.
Is there anyone that has some ideas on how to write this query?
Is it possible to write it with DQL language?
This doesn't take anything complex, if all records will always exist, you can inner join on all tables:
select
Book.id as book_id,
Book.book_title,
Teacher.id as teacher_id,
Teacher.teacher_name
from Book
inner join book_education_stage bes on bes.book_id = Book.id
inner join Education_Stage es on es.id = bes.education_stage_id
inner join teacher_education_stage tes on tes.book_id = Book.id
inner join Teacher on tes.teacher_id = Teacher.id
Result:
book_id
book_title
teacher_id
teacher_name
1
a
1
a
1
a
1
a
1
a
4
d
1
a
4
d
2
b
3
c
3
c
4
d
Here is the DB Fiddle if you're interested.

Selecting COUNT and MAX columns with 2 tables and a bridge table

so what I am trying to do is having 3 tables (pictures, collections, and bridge) with the following columns:
Collections Table:
| id | name |
------------------
| 1 | coll1 |
| 2 | coll2 |
------------------
Pictures Table: (timestamps are unix timestamps)
| id | name | timestamp |
-------------------------
| 5 | Pic5 | 1 |
| 6 | Pic6 | 19 |
| 7 | Pic7 | 3 |
| 8 | Pic8 | 892 |
| 9 | Pic9 | 4 |
-------------------------
Bridge Table:
| id | collection | picture |
-----------------------------
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 5 |
| 6 | 2 | 9 |
| 7 | 2 | 7 |
-----------------------------
And the result should look like this:
| collection_name | picture_count | newest_picture |
----------------------------------------------------
| coll1 | 4 | 8 |
| coll2 | 3 | 9 |
----------------------------------------------------
newest_picture should always be the picture with the heighest timestamp in that collection and I also want to sort the result by it. picture_count is obviously the count of picture in that collection.
Can this be done in a single statement with table joins and if yes:
how can I do this the best way?
A simple method uses correlated subqueries:
select c.*,
(select count(*)
from bridge b
where b.collection = c.id
) as pic_count,
(select p.id
from bridge b join
pictures p
on b.picture = b.id
where b.collection = c.id
order by p.timestamp desc
limit 1
) as most_recent_picture
from collections c;
A more common approach would use window functions:
select c.id, c.name, count(bp.collection), bp.most_recent_picture
from collections c left join
(select b.*,
first_value(p.id) over (partition by b.collection order by p.timestamp desc) as most_recent_picture
from bridge b join
pictures p
on b.picture = p.id
) bp
on bp.collection = c.id
group by c.id, c.name, bp.most_recent_picture;

Join 3 Tables in a MySql Query

i have this 3 tables in a MySql Database.
users
+----+------+--------+------+
| Id | Name | Status | Role |
+----+------+--------+------+
| 1 | A | Aktiv | Op |
| 2 | B | Aktiv | Op |
| 3 | C | Aktiv | Op |
| 4 | D | Aktiv | Op |
+----+------+--------+------+
cnt
+----+------+------------+------+
| Id | Name | Date | Type |
+----+------+------------+------+
| 1 | A | 2017-11-09 | Web |
| 2 | B | 2017-11-09 | Web |
| 3 | C | 2017-11-09 | Web |
| 4 | C | 2017-11-09 | Inb |
| 5 | A | 2017-11-09 | Web |
+----+------+------------+------+
Lead
+----+------+------------------+------------+
| Id | Name | Date | Type |
+----+------+------------------+------------+
| 1 | A | 2017-11-09 00:24 | Imported |
| 2 | B | 2017-11-09 09:32 | Activation |
| 3 | B | 2017-11-09 10:56 | Activation |
| 4 | D | 2017-11-09 12:21 | Activation |
| 5 | D | 2017-11-10 12:22 | Activation |
+----+------+------------------+------------+
I'm trying to join them in a main table but with no success, the query i'm using is:
SELECT IFNULL(u.Name,'Total') as "Name",
Sum(IF(c.Type = 'Web',1,0)) as "Cnt Web",
Sum(IF(l.Type = 'Activation',1,0)) as "Lead Web"
FROM users u
LEFT JOIN cnt c ON u.Name = c.Name and c.Date = '2017-11-09'
LEFT JOIN lead l ON u.Name = l.Name and l.Date>= '2017-11-09' AND l.Date< '2017-11-10'
WHERE u.Status = 'Aktiv' AND u.Role = 'Op'
GROUP BY u.Name WITH ROLLUP
The result i need is a table like this:
+----+------+--------+---------+
| Id | Name | Cnt Web| Lead Web|
+----+------+------------------+
| 1 | A | 2 | 0 |
| 2 | B | 1 | 2 |
| 3 | C | 1 | 0 |
| 4 | D | 0 | 1 |
+----+------+------------------+
When i try to join the first table with the second or the first with the third, i get the correct result, but i can't get the needed result when i join them all.
Any answer is the most welcomed. Thank you in advance.
Here's a solution using correlated sub-queries
SELECT u.Id,
u.Name,
(SELECT COUNT(Name) FROM cnt WHERE Name = u.name AND type = 'Web' AND Date = '2017-11-09') AS cnt_web,
(SELECT COUNT(Name) FROM lead WHERE Name = u.name AND type = 'Activation' AND Date>= '2017-11-09' AND Date< '2017-11-10') AS cnt_lead
FROM users u
WHERE u.Status = 'Aktiv' AND u.Role = 'Op'

How can I determine which user is the top one in the specific tag?

I have a question and answer website like stackoverflow. Here is the structure of some tables:
-- {superfluous} means some other columns which are not related to this question
// q&a
+----+-----------------+--------------------------+------+-----------+-----------+
| id | title | body | type | related | author_id |
+----+-----------------+--------------------------+------+-----------+-----------+
| 1 | How can I ... | I'm trying to make ... | q | NULL | 3 |
| 2 | | You can do that by ... | a | 1 | 1 |
| 3 | Why should I .. | I'm wonder, why ... | q | NULL | 1 |
| 4 | | First of all you ... | a | 1 | 2 |
| 5 | | Because that thing ... | a | 3 | 2 |
+----+-----------------+--------------------------+------+-----------+-----------+
// users
+----+--------+-----------------+
| id | name | {superfluous} |
+----+--------+-----------------+
| 1 | Jack | |
| 2 | Peter | |
| 3 | John | |
+----+--------+-----------------+
// votes
+----+----------+-----------+-------+-----------------+
| id | user_id | post_id | value | {superfluous} |
+----+----------+-----------+-------+-----------------+
| 1 | 3 | 4 | 1 | |
| 2 | 1 | 1 | -1 | |
| 3 | 2 | 1 | 1 | |
| 4 | 3 | 2 | -1 | |
| 5 | 1 | 4 | 1 | |
| 6 | 3 | 5 | -1 | |
+----+--------+-------------+-------+-----------------+
// tags
+----+------------+-----------------+
| id | name | {superfluous} |
+----+------------+-----------------+
| 1 | PHP | |
| 2 | SQL | |
| 3 | MySQL | |
| 4 | HTML | |
| 5 | CSS | |
| 6 | C# | |
+----+------------+-----------------+
// q&aTag
+-------+--------+
| q&aid | tag_id |
+-------+--------+
| 1 | 1 |
| 1 | 4 |
| 3 | 5 |
| 3 | 4 |
| 4 | 6 |
+-------+--------+
Now I need to find top users in a specific tag. For example, I need to find Peter as top user in PHP tag. Because his answer for question1 (which has PHP tag) has earned 2 upvotes. Is doing that possible?
Try this:
select q1.title, u.id, u.name, sum(v.value) total from `q&a` q1
left join `q&atag` qt ON q1.id = qt.`q&aid`
inner join tags t ON qt.tag_id = t.id
left join `q&a` q2 ON q2.related = q1.id
left join users u ON q2.author_id = u.id
left join votes v ON v.post_id = q2.id
where t.name = 'PHP'
group by q1.id, u.id
and here is a simple divided solution:
Let us divide it into sub queries:
get the id of the tag you will search for: select id from tags where name = 'PHP'
get the questions with this tag: select 'q&aid' from 'q&aTag' where tag_id = 1.
get the ids of answers for that question: select id, author_id fromq&awhere related in (2.)
get the final query: select user_id, sum(value) from votes where post_id in (3.) group by user_id
Now combining them all give the result:
select user_id, sum(`value`) total from votes
where post_id in (
select id from `q&a` where related in (
select `q&aid` from `q&aTag` where tag_id IN (
select id from tags where name = 'PHP'
)
)
)
group by user_id
you can add this at the end if you want only one record:
order by total desc limit 1

Include third table in LEFT JOIN query

I have five mysql tables. shops
+----+--------------+--------------+
| id | name | address |
+----+--------------+--------------+
| 1 | Shop1 | Street1 |
| 2 | Shop2 | Street2 |
| 3 | Shop3 | Street3 |
| 4 | Shop4 | Street4 |
+----+--------------+--------------+
fruits
+----+--------------+--------------+
| id | fruit | price |
+----+--------------+--------------+
| 1 | Bannana | 2.5 |
| 2 | Apple | 2.1 |
| 3 | Orange | 1.8 |
| 4 | Plum | 2.2 |
+----+--------------+--------------+
availability
+----+--------------+--------------+
| id | shop_id | fruit_id |
+----+--------------+--------------+
| 1 | 1 | 2 |
| 2 | 2 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
+----+--------------+--------------+
shop_activity
+----+--------------+--------------+--------------+
| id | shop_id | user_id | status |
+----+--------------+--------------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 3 | 2 | 1 |
| 3 | 1 | 2 | 2 |
| 4 | 2 | 2 | 1 |
+----+--------------+--------------+--------------+
users
+----+--------------+
| id | name |
+----+--------------+
| 1 | Peter |
| 2 | John |
+----+--------------+
I have query
SELECT
availability.shop_id,
shops.name
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
WHERE
fruit_id = 2
As a result I get name list of shops where fruit with id 2 (apple) is available.
What should I do so that I can include shop_activity table in query to get user's status if users.id = 1 beside proper shop. Something like this...
Shop1, NULL
Shop2, status: 1
You could try something like this:
SELECT
availability.shop_id,
shops.name,
shop_activity.status
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
LEFT JOIN shop_activity
ON shop_activity.shop_id = availability.shop_id
and shop_activity.user_id = 1
WHERE
fruit_id = 2
SELECT
availability.shop_id,
shops.name
FROM shops
LEFT JOIN availability ON availability.shop_id=shops.id
LEFT JOIN shop_activity ON shop_activity .shop_id=shops.id
WHERE
fruit_id = 2
and users.id=1
try making shops as the first table in left join
Try the following:
SELECT shops.name, shop_activity.status
FROM shops
INNER JOIN availability ON availability.shop_id = shops.id
AND availability.fruit_id = 2
LEFT JOIN shop_activity ON shops.shop_id = shop_activity.shop_id
AND shop_activity.user_id = 1
This should give you a row for every shop with apples, but the status will show as null for shops where the user has no activity, otherwise shows the status of that user.