Create a view from 2 tables with multiple subquery - mysql

I have two tables
table song
| ID | TITLE | AUTOR | CODE |
| 1 | title_1 | autor_1 | 123 |
| 2 | title_2 | autor_2 | 1332 |
| 3 | title_3 | autor_3 | 3434 |
table playlist
| ID | DATE | EVENT |
| 1 | 2020-05-01 | 123 |
| 2 | 2020-05-02 | 3434 |
| 3 | 2020-09-23 | 123 |
now I create a VIEW because table are very large
CREATE OR REPLACE VIEW View_song
AS
select c.id, c.title, c.autor, c.code,
( select
count(p.event) from playlist p
where p.event = c.code
) as total_counter
from song c
So I have a VIEW with the total songs + the column total_counter
| ID | TITLE | AUTOR | CODE | total_counter |
| 1 | title_1 | autor_1 | 123 | 2 |
| 2 | title_2 | autor_2 | 1332 | 0 |
| 3 | title_3 | autor_3 | 3434 | 0 |
I want to add a column to the VIEW so I want to have a more column with today_counter
| ID | TITLE | AUTOR | CODE | total_counter | today_counter |
| 1 | title_1 | autor_1 | 123 | 2 | 0 |
| 2 | title_2 | autor_2 | 1332 | 0 | 0 |
| 3 | title_3 | autor_3 | 3434 | 0 | 1 |
I try this query
CREATE OR REPLACE VIEW View_song
AS
select c.id, c.title, c.autor, c.code,
( select
count(p.event) from playlist p
where p.event = c.code
) as total_counter
( select
count(p.event) from playlist p
where p.event = c.code AND date = CURDATE()
) as today_counter
from song c
but it doesn't work

Use GROUP BY to these calculations in single go.
CREATE OR REPLACE VIEW View_song
AS
SELECT
c.id, c.title, c.author, c.code,
COUNT(p.`event`) AS total_counter,
SUM(IF(p.`date` = CURDATE(), 1, 0)) AS today_counter
FROM song c
LEFT JOIN playlist p ON c.`code` = p.`event`
GROUP BY c.id;
Add index on date, code and event to make it faster.
Also in your query, you're missing comma after total_counter.

Related

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;

How to get only one record in subquery for each record's max result

I have a user - course - exam database.
Table structures:
user table structure
+--------------------------------------------+
| user |
+--------------------------------------------+
| user_id | fullname | email |
+---------+--------------+-------------------+
| 1 | Test User 01 | test01#domain.com |
+---------+--------------+-------------------+
| 2 | Test User 02 | test02#domain.com |
+---------+--------------+-------------------+
| 3 | Test User 03 | test03#domain.com |
+---------+--------------+-------------------+
| 4 | Test User 04 | test04#domain.com |
+---------+--------------+-------------------+
course table structure
+-----------------------+
| course |
+-----------------------+
| course_id | title |
+-----------+-----------+
| 1 | Course 01 |
+-----------+-----------+
| 2 | Course 02 |
+-----------+-----------+
| 3 | Course 03 |
+-----------+-----------+
course_exam table structure (course can have one or more exam)
+----------------------------+
| course_exam |
+----------------------------+
| course_exam_id | course_id |
+----------------+-----------+
| 1 | 1 |
+----------------+-----------+
| 2 | 1 |
+----------------+-----------+
| 3 | 2 |
+----------------+-----------+
| 4 | 3 |
+----------------+-----------+
| 5 | 2 |
+----------------+-----------+
user_course_exam table structure (users can join one or more exam)
+---------------------------------------------------------------------------------------------------------------+
| user_course_exam |
+---------------------------------------------------------------------------------------------------------------+
| user_course_exam_id | course_exam_id | user_id | right_answer_total | wrong_answer_total | blank_answer_total |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
| 1 | 1 | 1 | 2 | 3 | 0 |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
| 2 | 1 | 1 | 4 | 1 | 0 |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
| 3 | 2 | 1 | 5 | 0 | 0 |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
| 4 | 1 | 1 | 3 | 1 | 1 |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
| 5 | 3 | 1 | 4 | 0 | 1 |
+---------------------+----------------+---------+--------------------+--------------------+--------------------+
I should prepare a report like below:
user_id
fullname
email
completed_course_total (course total having completed exams by user)
remaining_course_total
right_answer_total (Max right answer for best scored exam. Only one result should fetch for each course)
Expected report result
+-------------------------------------------------------------------------------------------------------------------+
| user_id | fullname | email | completed_course_total | remaining_course_total | right_answer_total |
+---------+--------------+-------------------+------------------------+------------------------+--------------------+
| 1 | Test User 01 | test01#domain.com | 2 | 1 | 13 |
+---------+--------------+-------------------+------------------------+------------------------+--------------------+
| 2 | Test User 02 | test02#domain.com | 0 | 3 | 0 |
+---------+--------------+-------------------+------------------------+------------------------+--------------------+
| 3 | Test User 03 | test03#domain.com | 0 | 3 | 0 |
+---------+--------------+-------------------+------------------------+------------------------+--------------------+
| 4 | Test User 04 | test04#domain.com | 0 | 3 | 0 |
+---------+--------------+-------------------+------------------------+------------------------+--------------------+
This is my query, but right answer result returns all of exams. I want get only sum of max right answer total for each courses.
Please also consider these cases:
A course can have more than one exams.
A user can join more than one course_exam.
SELECT DISTINCT
`user`.user_id,
(
SELECT COUNT(DISTINCT(`user_course_exam`.`course_exam_id`)) FROM `user_course_exam`
INNER JOIN `course_exam` ON (`course_exam`.`course_exam_id` = `user_course_exam`.`course_exam_id`)
WHERE `user_course_exam`.`user_id` = `user`.`user_id` && `course_exam`.course_id IN (
SELECT course_id FROM course_exam
WHERE status = '1'
GROUP BY `course_exam`.`course_id`
)
) AS completed_course,
(
SELECT SUM(`user_course_exam`.`right_answer_total`) FROM `user_course_exam`
INNER JOIN `course_exam` ON (`course_exam`.`course_exam_id` = `user_course_exam`.`course_exam_id`)
WHERE `user_course_exam`.`user_id` = `user`.`user_id` && `course_exam`.course_id IN (
SELECT course_id FROM course_exam
WHERE status = '1'
GROUP BY `course_exam`.`course_id`
) ORDER BY `user_course_exam`.`right_answer_total` DESC
) AS right_answer
FROM
`user`
WHERE
`user`.`user_id` > 0
GROUP BY
`user`.`user_id`
ORDER BY
`user`.`user_id` ASC
LIMIT 15 OFFSET 0
JSFiddle: http://sqlfiddle.com/#!9/72ee15/1/0
You can try to use MAX() than SUM()
SELECT DISTINCT `user`.`user_id`,
(
SELECT COUNT(DISTINCT (`user_course_exam`.`course_exam_id`))
FROM `user_course_exam`
INNER JOIN `course_exam`
ON (`course_exam`.`course_exam_id` = `user_course_exam`.`course_exam_id`)
WHERE `user_course_exam`.`user_id` = `user`.`user_id` && `course_exam`.`course_id` IN (
SELECT `course_id`
FROM `course_exam`
GROUP BY `course_exam`.`course_id`
)
) AS `completed_course`,
(
SELECT sum(`all_curse`.`max_right_answer`)
FROM (
SELECT MAX(`user_course_exam`.`right_answer_total`) AS `max_right_answer`,
`user_course_exam`.`user_id`
FROM `user_course_exam`
INNER JOIN `course_exam`
ON `course_exam`.`course_exam_id` = `user_course_exam`.`course_exam_id`
WHERE `course_exam`.`status` = 1
GROUP BY `course_exam`.`course_id`, `course_exam`.`course_exam_id`
) AS `all_curse`
WHERE `all_curse`.`user_id` = `user`.`user_id`
) AS `right_answer`
FROM `user`
WHERE `user`.`user_id` > 0
GROUP BY `user`.`user_id`
ORDER BY `user`.`user_id`
LIMIT 15 OFFSET 0
JSFiddle: Here

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

Left join select using Propel ORM

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

mysql - join problem

I have two tables like below,
mysql> select * from Books ;
+----+------+------------+----------+----------+
| id | name | author_name| category | category2|
+----+------+------------+----------+----------+
| 1 | 1 | Steve | CT001 | CT003 |
| 2 | 2 | John | CT002 | CT002 |
| 3 | 3 | Larry | CT003 | CT002 |
| 4 | 3 | Michael | CT004 | CT004 |
| 5 | NULL | Steven | CT005 | CT005 |
+----+------+------------+----------+----------+
mysql> select * from Codemst ;
+----+------+------------+
| id | code | name |
+----+------+------------+
| 1 | CT001| fiction |
| 2 | CT002| category1 |
| 3 | CT003| etc |
| 4 | CT004| etc2 |
| 5 | CT005| etc3 |
+----+------+------------+
I want to get human readable category name when I query like "select * from Books;"
If there was only one category in the Books table, I think I can use "Join" but, in this case what can I do?
select * from Books b
Inner Join Codemst c1 on b.category = c1.code
inner join codemst c2 on b.category2 = c2.code;
c1.name will hold the readable category, and c2.name the readable category2