here's my (simplified) table structure:
table: category_main
id name
-------------
1 food
2 vegetable
table category_sub
id id_catmain name
---------------------
10 1 cake
11 1 chocolate
12 1 burger
13 2 apple
14 2 banana
table images
id id_catsub filename views
-------------------------------------
1 10 cake1.jpg 11
2 10 cake2.jpg 24
3 10 cake3.jpg 65
4 11 chocolate1.jpg 31
5 11 chocolate2.jpg 62
6 11 chocolate3.jpg 32
7 11 chocolate4.jpg 58
8 12 burger1.jpg 23
9 12 burger2.jpg 43
10 12 burger3.jpg 76
11 13 apple1.jpg 29
11 13 apple2.jpg 67
11 14 banana1.jpg 78
desired output:
id name total_views
----------------------------
1 food 425
2 vegetable 174
as you can see i want to get the total views for each main category.
currently i'm running a loop for each subcategory but there must be an easier and faster way :/
thanks
Double LEFT JOIN + aggregation will do the job.
SELECT cm.id, cm.name, sum(images.views) as views
FROM category_main as cm
LEFT JOIN category_sub as cs ON cs.id_catmain = cm.id
LEFT JOIN images ON images.id_carsub = cs.id
GROUP BY cm.id
ORDER BY views DESC;
LEFT JOIN (instead of JOIN) will make you sure that you have all categories listed even if there's no subcategory or image in it. If you don't want empty categories to be listed, then use JOIN.
SELECT c.id AS id, c.name AS name, sum(i.views) AS total_views
FROM category_main c, category_sub s, images i
WHERE c.id=s.id_catmain and s.id=i.id_catsub
GROUP BY c.id,c.name;
simply join the three tables, and then you can sum the views grouped by the id's:
select cm.id, cm.name, sum(i.views) as total_views from
category_main as cm inner join category_sub as cs on cm.id = cs.id_catmain
inner join cs.id = i.id_catsub group by cm.id
Related
I have the following tables in a MySQL database:
team
team_id name
3 Rangers
12 Capitals
19 Red Wings
4 Bruins
212 Avalanche
102 Flyers
20 Islanders
50 Sabres
7 Stars
player
id name
2 Zach
1 Deb
17 William
9 Viktor
12 Andrew
41 Chris
22 Bobby
5 Phil
3 Roy
92 Li
6 Juan
players_in
team_id player_id points
3 2 42
212 2 19
3 12 18
19 12 2
3 41 2
4 41 1
212 41 78
212 17 1
19 41 4
12 41 2
3 17 6
4 1 9
102 1 40
102 22 7
20 22 19
20 5 22
50 3 20
12 92 15
12 17 8
7 6 12
Here is a SQL Fiddle with the data: http://www.sqlfiddle.com/#!9/989ebe/1
I would like to get the name and id of the players who have played on ALL of the teams that Zach has played on.
In this case, Zach has played for the Rangers and the Avalanche.
Therefore, the desired result set would be:
name id
William 17
Chris 41
(because these players were part of both the Rangers and the Avalanche teams)
How would I do this?
Thanks!
select distinct p.*
from player p
join players_in pi on pi.player_id = p.id
join player p2 on p2.name = 'Zach'
join players_in pi2 on pi2.team_id = pi.team_id
and pi2.player_id = p2.id
where
p.name <> 'Zach'
and not exists (select 1 from players_in pi3
where pi3.player_id = p2.id
and pi3.team_id not in (select team_id
from players_in pi4
where pi4.player_id = p.id));
First of all I've joined players_in (pi) with players (p) obtaining the set of all players and theirs teams.
Second, cross joined player zack joined with player_in (pi2) obtaining the set of Zach's teams. Joined pi2 with pi I've obtained the set of all player that had played in a Zach's team.
Now the where conditions:
p.name <> 'Zach' will exclude Zach from my list.
The not exists condition is the hard part of the query.
I've selected all Zach teams again (pi3) not in the set of the player's (p) team,
SQL Fiddle here
Your requirement could be translated to: searching for players which there's not exists any Jack's team that they don't play in. Corresponding query could be:
SELECT
DISTINCT p1.name, p1.id
FROM
player p1
INNER JOIN players_in pin1 ON p1.id = pin1.player_id
WHERE
name != 'Zach'
AND NOT EXISTS (
SELECT 1
FROM
team t
INNER JOIN players_in pin2 ON t.team_id = pin2.team_id
INNER JOIN player p2 ON p2.id = pin2.player_id
WHERE
p2.name = 'Zach'
AND NOT EXISTS (SELECT 1
FROM players_in pin3
WHERE pin2.team_id = pin3.team_id
AND pin1.player_id = pin3.player_id)
);
Demo: http://www.sqlfiddle.com/#!9/989ebe/61
Using a cte for Zach's games and then checking all potential memberships based on team_id existence in the cte's values:
with cte as (
select pi1.team_id from players_in pi1 join player p2 on p2.id = pi1.player_id
where p2.name = 'Zach'
)
select p.* from player p where (select count(*) from cte c) = (select
sum(pi1.team_id in (select c.team_id from cte c))
from players_in pi1 where pi1.player_id = p.id) and p.name != 'Zach'
See fiddle.
table VOTES
id
voters_id
candidate_id
positions_id
1
xxx
18
6
2
xxx
18
6
3
xxx
18
6
4
xxx
18
6
5
xxx
19
6
6
xxx
19
6
7
xxx
22
20
8
xxx
22
20
table POSITIONS
id
title
6
president
20
mayor
table candidates
id
name
18
mark
19
john
22
eddie
I HAVE THESE THEERE TABLES, I NEED A QUERY FOR THIS OUTPUT
total_votes
candidate_id
candidate_name
position_id
position_name
4
18
mark
6
president
2
19
mark
6
president
2
22
eddie
20
mayor
Use:
select v.total_votes,
v.candidate_id,
c.name as candidate_name,
v.position_id ,
p.title as position_name
from (select COUNT(*) as total_votes,
candidate_id,
position_id
from votes
GROUP BY candidate_id, position_id
) as v
INNER JOIN positions p on v.position_id=p.id
INNER JOIN candidates c on c.id=v.candidate_id ;
Result:
total_votes candidate_id candidate_name position_id position_name
4 18 mark 6 president
2 19 john 6 president
2 22 eddie 20 mayor
Note. Aggregation always comes after the join, that's why you need to do the aggregation on a subquery
https://dbfiddle.uk/7q9GUm0y
can i ask, if i would add the percentage, counting the SUM of total
votes, on the votes obteined from a single candidate, how should i do
it?
select v.total_votes,
round(((v.total_votes * 100) / temp.tot_voters),2) AS Percentage,
concat(round(((v.total_votes * 100) / temp.tot_voters),2),'%') AS Percentage_1,
v.candidate_id,
c.name as candidate_name,
v.position_id ,
p.title as position_name
from (select COUNT(*) as total_votes,
candidate_id,
position_id
from votes
GROUP BY candidate_id, position_id
) as v
CROSS JOIN (select count(voters_id) as tot_voters from votes) temp
INNER JOIN positions p on v.position_id=p.id
INNER JOIN candidates c on c.id=v.candidate_id ;
https://dbfiddle.uk/wBTbVVlf
I have 4 tables (1 to many):
Dont say anything about that "email" relation. It is how my developer boss built it years ago.
EMPLOYEES (+-50 results)
------------------------------------------------
id name
1 EmpName 1
2 EmpName 2
CUSTOMERS (+50k results)
------------------------------------------------
id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES (+650k results)
------------------------------------------------
id customer_email category_id
1 john#doe.com 97
2 john#doe.com 13
3 donald#duck.com 56
4 donald#duck.com 126
5 donald#duck.com 45
INTERESTS_PRODUCTS (+650k results)
------------------------------------------------
id customer_email product_id
1 john#doe.com 78
2 john#doe.com 23
3 donald#duck.com 19
4 donald#duck.com 56
5 donald#duck.com 45
So I need to filter the customers by their assigned employee and their interests.
And here is the query:
SELECT
*
FROM
(
SELECT
customers.id AS 'id',
customers.name AS 'first_name',
customers.email,
employees.id AS 'employee_id'
FROM
customers,
employees
WHERE
employees.id = 2
AND
customers.employee_assigned = employees.id
) AS myCustomers
LEFT JOIN interests_categories
ON interests_categories.customer_email = myCustomers.email
LEFT JOIN interests_products
ON interests_categories.customer_email = myCustomers.email
WHERE
(
interests_categories.category_id = 20
OR
interests_categories.category_id = 21
)
GROUP BY myCustomers.email
So, the problem:
If the employee has a low number of assigned customers (like 3) query
is successfull.
If the employee has a medium-high number of assigned customers (over 100) query stucks.
I execute SHOW PROCESSLIST and it is stucked "Generating temp table".
Anyone has idea? :(
Thank you.
Check the indexes on your tables and try this:
SELECT
c.id AS 'id',
c.name AS 'first_name',
c.email,
e.id AS 'employee_id'
ic.*,
ip.*
FROM customers c
JOIN employees e
ON c.employee_assigned = e.id
LEFT JOIN interests_categories ic
ON ic.customer_email = c.email
LEFT JOIN interests_products ip
ON ic.customer_email = c.email
WHERE
(
ic.category_id IN (20,21)
AND e.id = 2
)
GROUP BY myCustomers.email
Incidentally, a less dumb design might look like as follows. If it was me, I'd start with this, and provide properly representative CREATE and INSERT statements accordingly. Also, I'm curious about where category_id comes from - because that's potentially an area for further optimization.
EMPLOYEES
------------------------------------------------
employee_id name
6 EmpName 1
12 EmpName 2
CUSTOMERS
------------------------------------------------
customer_id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES
------------------------------------------------
customer_id category_id
1 97
1 13
2 56
2 126
2 45
INTERESTS_PRODUCTS
------------------------------------------------
customer_id product_id
1 78
1 23
2 19
2 56
2 45
I`m running this query below:
SELECT a.id as id_order, b.id as id_service,
d.nome as service_name,
c.dente as tooth,
(SELECT count(c.dente)
FROM labpedidoservicodente c
WHERE b.id = c.idLabPedidoServico) AS total,
e.valorServico as cost
FROM labpedido a
INNER JOIN labpedidoservico b
ON a.id = b.idLabPedido
INNER JOIN labpedidoservicodente c
ON a.id = c.idLabPedido
INNER JOIN labservico d
ON b.idLabServico = d.id
INNER JOIN labservicovalor e
ON b.idLabServico = e.idLabServico
WHERE a.id = '914'
My result comes this way:
order_id service_id service_name tooth total cost
914 640 SERVICE NAME 1 11 3 80.00
914 640 SERVICE NAME 1 21 3 80.00
914 640 SERVICE NAME 1 38 3 80.00
914 641 SERVICE NAME 2 11 3 84.80
914 641 SERVICE NAME 2 21 3 84.80
914 641 SERVICE NAME 2 38 3 84.80
My desired output should be like this:
order_id service_id service_name tooth total cost
914 640 SERVICE NAME 1 11-21 2 80.00
914 641 SERVICE NAME 2 38 1 84.60
The problem is that I need to concat these rows in the column "tooth" inside their respective "service_id", have tried everything but no sucess, also the total
Replace c.dente with GROUP_CONCAT(c.dente SEPARATOR ' - ') and add GROUP BY service_id below.
I need to know student - student ID, first name, last name. Those have not seen an event at a particular given Auditorium. I have to list these students in ascending last name order (through one query). Run the query twice, showing results for the Auditorium “London Theatre” in one run and “Brentwood Hall” in the second run.
I have no clue what this first and second run means. Also please check if the below will work for first run. I am thinking of creating a view and storing the first run, and writing the same query with "Brentwood Hall" and storing it in the second view. However, how will I display 2 views as part of same query then?
SELECT op.StudentID
, s.FirstName
, s.LastName
FROM auditorium a
JOIN audievent e
ON a.auditoriumID = e.AuditoriumID
JOIN reserver r
ON e.EventID = r.EventID
JOIN OrderProcessor op
ON r.OrderID = op.OrderID
JOIN Student s
ON op.StudentID = s.StudentID
WHERE op.StudentID NOT IN ( SELECT DISTINCT op.StudentID
FROM OrderProcessor op
LEFT
JOIN reserver r
ON op.OrderID = r.OrderID
LEFT
JOIN AudiEvent e
ON r.AuditoriumID = e.AuditoriumID
WHERE e.EventID NOT IN ( SELECT EventID FROM reserver)
)
AND a.AuditoriumName = 'London Theatre'
GROUP
BY op.StudentID;
OUTPUT:-
StudentID FirstName LastName
ID1 Andy Hall
ID2 Andy Halls
ID3 Mush Peters
ID4 Garry Hiegl
INPUT:-
Auditorium
AudiID AudiName
Audi01 London Theatre
Audi02 Brentwood Hall
Audi03 County Hall
AudiEvent
EventID AudiID
1 Audi01
2 Audi01
3 Audi01
4 Audi01
5 Audi01
6 Audi01
7 Audi01
8 Audi01
9 Audi01
10 Audi01
11 Audi02
12 Audi02
13 Audi02
14 Audi02
15 Audi02
16 Audi03
17 Audi03
18 Audi03
19 Audi03
20 Audi03
Reserver
ReserverID OrderID AudiID EventID
1 1 Audi01 1
2 1 Audi01 2
3 2 Audi01 3
4 2 Audi01 4
5 2 Audi02 14
6 3 Audi02 15
7 3 Audi02 11
8 4 Audi03 18
9 4 Audi03 19
10 4 Audi03 20
OrderProcessor
OrderID StudentID
1 ID01
2 ID02
3 ID03
4 ID04
Student
StudentID FirstName LastName
ID1 Andy Hall
ID2 Andy Halls
ID3 Mush Peters
ID4 Garry Hiegl
As #wildplasser mentioned, NOT EXISTS is perfect for this:
SELECT studentId, student.firstName, student.lastName
FROM Student
WHERE NOT EXISTS (SELECT 1
FROM OrderProcessor
JOIN Reserver
ON Reserver.orderId = OrderProcessor.orderId
JOIN AudiEvent
ON AudiEvent.eventId = Reserver.eventId
JOIN Auditorium
ON Auditorium.audiId = AudiEvent.audiId
AND Auditorium.audiName = 'London Theatre'
WHERE OrderProcessor.studentId = Student.studentId)
(working SQL Fiddle example)
The necessary changes to get students who haven't seen events at the 'Brentwood Hall' is left as an exercise to the reader.