MYSQL Stuck Generating temp table (massive query) - mysql

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

Related

in a left join is it possible set limit,offset for the base table only

in a left join is it possible set limit,offset for the base table only
I'm using the sample db nwind with mysql/linux
I'm trying to limit the the rows of the 'base' TABLE (customers) IN a left join
I know I can rewrite the query (as a lot of others answers)
But I'm asking if there is a solution with a join
I have a class that do paging and filtering automatically.
running the query without limit,offset
select
customers.id,
customers.company,
customers.job_title,
orders.id,
orders.order_date
FROM customers
LEFT OUTER JOIN orders ON customers.id = orders.customer_id
WHERE customers.job_title='Owner'
ORDER BY customers.id,orders.id
I got the expected result
id company job_title id order_date
1 Company A Owner 44 2006-03-24 00:00:00
1 Company A Owner 71 2006-05-24 00:00:00
2 Company B Owner NULL NULL
5 Company E Owner NULL NULL
7 Company G Owner 41 2006-03-24 00:00:00
7 Company G Owner 68 2006-05-24 00:00:00
17 Company Q Owner NULL NULL
24 Company X Owner NULL NULL
#example 1
select
customers.id,
customers.company,
customers.job_title,
orders.id,
orders.order_date
FROM customers
LEFT OUTER JOIN orders ON customers.id = orders.customer_id
WHERE customers.job_title='Owner'
ORDER BY customers.id,orders.id
limit 3 offset 0
d company job_title id order_date
1 Company A Owner 44 2006-03-24 00:00:00
1 Company A Owner 71 2006-05-24 00:00:00
2 Company B Owner NULL NULL
#example 2
select
customers.id,
customers.company,
customers.job_title,
orders.id,
orders.order_date
FROM customers
LEFT OUTER JOIN orders ON customers.id = orders.customer_id
WHERE customers.job_title='Owner'
ORDER BY customers.id,orders.id
limit 3 offset 3
id company job_title id order_date
5 Company E Owner NULL NULL
7 Company G Owner 41 2006-03-24 00:00:00
7 Company G Owner 68 2006-05-24 00:00:00
for eaxample 1 I want to get
id company job_title id order_date
1 Company A Owner 44 2006-03-24 00:00:00
1 Company A Owner 71 2006-05-24 00:00:00
s2 Company B Owner NULL NULL
(limit 3
for example 2
id company job_title id order_date
5 Company E Owner NULL NULL
7 Company G Owner 41 2006-03-24 00:00:00
7 Company G Owner 68 2006-05-24 00:00:00
limit 3 offset 3
I think by limit base table you mean left table
You can do it like this
select * from (
select
id,
company,
job_title
FROM customers
LIMIT 3
OFFSET 0
) as customers
LEFT OUTER JOIN orders
ON customers.id = orders.customer_id
WHERE customers.job_title='owner'
ORDER BY customers.id,orders.id
The first bracket acts as your base table now with limited rows

How to join to single row in one to many relationship in SQL?

I want to join one to many table with single row on many table by limit 1 and order by create date
tbl_cart :
id fullname
1 myname1
2 myname2
3 myname3
tbl_cart_status:
id cart_id status created_at
1 1 33 2018-09-20
2 1 34 2018-09-23
3 2 34 2018-09-21
4 1 100 2018-09-25
5 2 35 2018-09-29
How can i get output with sql like this:
I want to get lastest status of my cart by ordered with created_at column
myname cart_id status created_at
myname1 1 100 2018-09-25
myname2 2 35 2018-09-29
Think filtering for this type of query:
select c.name, cs.*
from tbl_cart c join
tbl_cart_status cs
on c.id = cs.cart_id
where cs.created_at = (select max(cs2.created_at)
from tbl_cart_status cs2
where cs2.cart_id = cs.cart_id
);

mysql query: how to

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

How to show only the id of one table and not to duplicate with other tables?

I have 6 tables and 3 of them is what I need to show in my table and the other 3 is what I needed to hide in the same table.
Report:
id name branch comp_id start_date end_date
100 A 001 011 2022-08-14 2022-08-14
200 B 002 012 2022-08-14 2022-08-14
Report Details:
id product_id product_code price deliveries
100 01 11 20.00 10
100 01 11 20.00 10
200 01 11 20.00 20
200 02 12 25.00 20
Products:
id code name
01 11 Prod 1
02 12 Prod 2
Product Details:
id code name Desc
01 11 Prod 1 Desc 1
02 12 Prod 2 Desc 2
Branches:
id code name
001 021 Branch 1
002 022 Branch 2
Companies:
id name branch
011 Company 1 021
012 Company 2 022
I want the output to be like this:
id: will come from reports table
branch_name: will come from branches table using the branch in report table
company_name: will come company using the comp_id in report table
product_name: will come from products table using product_id in report details table
description: will come from product_details table using product_code in report details table
start_date: will come from report table
end_date: will come from report table
id branch_name company_name product_name description start_date end_date
100 branch 1 Company 1 Prod 1 Desc 1 2022-08-14 2022-08-14
200 branch 2 Company 2 Prod 1 Desc 1 2022-08-14 2022-08-14
200 branch 2 Company 2 Prod 2 Desc 2 2022-08-14 2022-08-14
I have this sql and it all shows the id in report details table:
SELECT *, `acc`.`name` AS `cname`, `out`.`name` AS `outname`, `pro`.`name` AS `pname`, `prod`.`name` AS `sname`
FROM `report` AS `rep`
JOIN `companies` AS `acc` ON `rep`.`account_id`=`acc`.`code`
JOIN `branches` AS `out` ON `rep`.`outlet_id`=`out`.`code`
JOIN `report_details` AS `red` ON `rep`.`report_id`=`red`.`report_id`
JOIN `products` AS `pro` ON `red`.`product_id`=`pro`.`id`
JOIN `product_details` AS `prod` ON `red`.`sku_id`=`prod`.`id`
Try this:
SELECT R.id, B.name AS branch_name, C.name AS company_name, PD.product_name AS product_name, PD.Desc AS description, R.start_date
FROM Report R
LEFT JOIN report_details RD ON R.id = R.id
LEFT JOIN products P ON RD.product_id = P.id
LEFT JOIN product_details PD ON PD.product_id = PD.id
LEFT JOIN branches B ON R.branch = B.id
LEFT JOIN companies C ON R.comp_id = C.id
GROUP BY R.id, B.name, C.name, PD.product_name, PD.Desc, R.start_date

Add total of 3 rows for specific id

I have three tables:
Students
-------------------------------------------------------------
studentId first last gender weight
-------------------------------------------------------------
1 John Doe m 185
2 John Doe2 m 130
3 John Doe3 m 250
Lifts
-------------------
liftId name
-------------------
1 Bench Press
2 Power Clean
3 Parallel Squat
4 Deadlift
5 Shoulder Press
StudentLifts
------------------------------------------------
studentLiftId studentId liftId weight
------------------------------------------------
1 1 1 185
2 2 3 130
3 3 1 190
4 1 2 120
5 2 1 155
6 3 2 145
7 1 1 135
8 1 1 205
9 2 3 200
10 1 3 150
11 2 2 110
12 3 3 250
I would like to have four top lists:
Bench Press
Parallel Squat
Power Clean
Total of the above 3
I can successfully grab a top list for each specific lift using the following query:
SELECT s.studentId, s.first, s.last, s.gender, s.weight, l.name, sl.weight
FROM Students s
LEFT JOIN (
SELECT *
FROM StudentLifts
ORDER BY weight DESC
) sl ON sl.studentId = s.studentId
LEFT JOIN Lifts l ON l.liftId = sl.liftId
WHERE l.name = 'Bench Press'
AND s.gender = 'm'
AND s.weight > 170
GROUP BY s.studentId
ORDER BY sl.weight DESC
However, I am stuck on how to add the highest total of each lift for each student. How can I first find the highest total for each student in each lift, and then add them up to get a total of all three lifts?
Edit
The result set that I am looking for would be something like:
-------------------------------------------------
studentId first last weight
-------------------------------------------------
3 John Doe3 585
1 John Doe 475
2 John Doe2 465
I also forgot to mention that I would actually like two lists, one for students above 170 and one for students below 170.
SELECT -- join student a total weight to the student table
A.studentId,
A.first,
A.last,
C.totalWeight
FROM
Student A,
(
SELECT -- for each studet add the max weights
sum(B.maxWeight) as totalWeight,
B.studentID
FROM (
SELECT -- for each (student,lift) select the max weight
max(weight) as maxWeight,
studentId,
liftID
FROM
StudentLifts
GROUP BY
studentId,
liftID
) B
GROUP BY
studentId
) C
WHERE
A.studentID = C.studentId
-- AND A.weight >= 170
-- AND A.weight < 170
-- pick one here to generate on of the two lists.