MySQL query very slow on group by and count - mysql

I have a database with the following tables:
hospitals (~28K rows),
inspections (~116K rows),
issues (~290K rows)
Hospitals have inspections and each inspection has zero or more issues. I have the following query:
SELECT count(*) as count, BName, BCity, BAddress, BState, BZip, Ins_date, BCountry, Ins_Type
FROM ( SELECT b.id as ID, b.hospital_name as BName, b.city as BCity, b.address as BAddress, b.country as BCountry, b.state as BState, b.zip as
BPostal, i.date as Ins_date, v.type as Ins_Type
FROM hospital_table b, inspection_table i, issue_table v
WHERE b.id = i.business_id
AND i.id = v.inspection_id
ORDER BY b.hospital_name, i.date DESC ) AS sumissues
GROUP BY ID
ORDER BY count DESC;
The output I expect and get is:
112 | Burnaby Memorial | Burnaby | 3935 Kincaid St | BC | 2017-07-19 | Canada | Cleanliness
The problem is it takes about 40seconds to run. I have an index on the foreign keys, and inspection_table.date. Any ideas on how I can optimize this?

Looking to your code
The order by in subselect not needed and also the subselect is not needed
SELECT
count(*) as count
, b.hospital_name as BName
, b.city as BCity
, b.address as BAddress
, b.country as BCountry
, b.state as BState
, b.zip as BPostal
, i.date as Ins_date
, v.type as Ins_Type
FROM hospital_table b
INNER JOIN inspection_table i ON b.id = i.business_id
INNER JOIN issue_table v ON i.id = v.inspection_id
GROUP BY b.id
ORDER BY count DESC, b.hospital_name, i.date
But be careful for what in commente by #User_by_Already
for this and for avoi more rows group for the others column and if you don'care for the result of these columns you can use (fake) aggregation function
SELECT
count(*) as count
, b.hospital_name as BName
, b.city as BCity
, b.address as BAddress
, b.country as BCountry
, b.state as BState
, b.zip as BPostal
, min(i.date) as Ins_date
, min(v.type) as Ins_Type
FROM hospital_table b
INNER JOIN inspection_table i ON b.id = i.business_id
INNER JOIN issue_table v ON i.id = v.inspection_id
GROUP BY b.id
ORDER BY count DESC, b.hospital_name, i.date

Related

Rank actors with movies released in India based on their average ratings. Which actor is at the top of the list?

-- Note: The actor should have acted in at least five Indian movies. -- (Hint: You should use the weighted average based on votes. If the ratings clash, then the total number of votes should act as the tie breaker
SELECT n.name as actor_name
, r.total_votes
, COUNT(r.movie_id) as movie_count
, r.avg_rating as actor_avg_rating
, RANK() OVER( PARTITION BY
rm.category = 'actor'
ORDER BY
r.avg_rating DESC
) actor_rank
FROM names as n
JOIN role_mapping as rm
ON n.id = rm.movie_id
JOIN movie as m
ON m.id = rm.movie_id
JOIN ratings as r
ON r.movie_id = m.id
where m.country regexp '^INDIA$'
and m.languages regexp '^HINDI$'
group
by actor_name
having count(rm.movie_id) >= 5;
The output gives no error but no result too.
This would work:
SELECT a.name as actor_name, c.total_votes, COUNT(c.movie_id) as movie_count,c.avg_rating as actor_avg_rating,
RANK() OVER( PARTITION BY
d.category = 'actor'
ORDER BY
c.avg_rating DESC
) actor_rank
FROM names a, movie b, ratings c, role_mapping d
where b.country = 'INDIA'
and b.id = c.movie_id
and b.id= d.movie_id
and a.id = d.name_id
group by actor_name
having count(d.movie_id) >= 5
order by actor_avg_rating desc
;
You had tried joining nameid with movie id which is the mistake
SELECT NAME AS actor_name,
Cast(Sum(total_votes)/Count(movie_id) AS DECIMAL(8,0)) AS total_votes,
Count(movie_id) AS movie_count,
avg_rating AS actor_avg_rating,
Dense_rank() OVER(ORDER BY avg_rating DESC) AS actor_rank
FROM names n INNER JOIN role_mapping r ON n.id=r.name_id
INNER JOIN ratings using (movie_id) INNER JOIN movie m ON m.id=r.movie_id
WHERE country="india" AND category="actor"
GROUP BY actor_name
HAVING Count(movie_id)>=5;
WITH top_actor
AS (SELECT b.NAME
AS
actor_name,
Sum(c.total_votes)
AS
total_votes,
Count(DISTINCT a.movie_id)
AS
movie_count,
Round(Sum(c.avg_rating * c.total_votes) / Sum(c.total_votes), 2)
AS
actor_avg_rating
FROM role_mapping a
INNER JOIN names b
ON a.name_id = b.id
INNER JOIN ratings c
ON a.movie_id = c.movie_id
INNER JOIN movie d
ON a.movie_id = d.id
WHERE a.category = 'actor'
AND d.country LIKE '%India%'
GROUP BY a.name_id,
b.NAME
HAVING Count(DISTINCT a.movie_id) >= 5)
SELECT *,
Rank()
OVER (
ORDER BY actor_avg_rating DESC) AS actor_rank
FROM top_actor;

MySQL IN Statement + return row with most matches

I have the following SQL Query - note: the IN words change for different queries:
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia");
I get 3 returns.
2 match 'australia' and 1 matches 'donvale' and 'australia'.
Is there a way for me to return or order by the highest matches?
I can manipulate the results with PHP but would be great if I could do this with SQL alone.
cheers
Use limit to get the top record:
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
Group by a.city Order by count(a.city) desc
limit 1 ;
Hmmm, is this what you mean?
SELECT w.countryCode, count(*)
FROM geoWorld w JOIN
geoCountry c
ON w.countryCode = c.countryCode
WHERE w.city IN ('free', 'dating', 'donvale', 'australia')
GROUP BY w.countryCode
ORDER BY count(*) DESC;
Use "group by":
select a.pid,a.city,a.countryCode, b.zipEnabled,b.english, a.c from (
SELECT a.pid,a.city,a.countryCode, count(*) c
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
group by a.pid,a.city,a.countryCode
) AS a JOIN geoCountry AS b ON a.countryCode=b.countryCode
order by c desc
or
SELECT a.pid,a.city,a.countryCode,b.zipEnabled,b.english,
(select count(*) from geoCountry c where c.countryCode = a.countryCode) ct
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE
a.city IN ("free","dating","donvale","australia")
order by ct desc
or if you "need to return the row that matches 2 values in the IN Statement" use having statement, e.g.:
select a.pid,a.city,a.countryCode, b.zipEnabled,b.english, a.c from (
SELECT a.pid,a.city,a.countryCode, count(*) c
FROM geoWorld AS a
JOIN geoCountry AS b ON a.countryCode=b.countryCode
WHERE a.city IN ("free","dating","donvale","australia")
group by a.pid,a.city,a.countryCode
having c=2
) AS a JOIN geoCountry AS b ON a.countryCode=b.countryCode
order by c desc

City_id in the where clause is ambiguous

I have wrote the query , but it is showing city id in the where clause is ambigious
SELECT
`a`.`name`,
`a`.`company`,
`a`.`city`,
`a`.`country`,
`a`.`phone`,
`a`.`type_of_enquiry`,
`b`.`enquiry_id`,
`a`.`hearaboutus`,
`a`.`email`,
`a`.`comments`,
`a`.`address`,
`c`.`id`,
`c`.`city_id`
FROM (`sobha_enquiry` a)
LEFT JOIN `sobha_enquiryzone` b
ON `b`.`enquiry_id` = `a`.`id`
LEFT JOIN `sobha_admin` c
ON `c`.`city_id` = `b`.`city_id`
WHERE `city_id` = '2'
GROUP BY `a`.`id`
ORDER BY `id` desc, `a`.`id` DESC
Pls help me !!!!!!!!!!
SELECT
`a`.`name`,
`a`.`company`,
`a`.`city`,
`a`.`country`,
`a`.`phone`,
`a`.`type_of_enquiry`,
`b`.`enquiry_id`,
`a`.`hearaboutus`,
`a`.`email`,
`a`.`comments`,
`a`.`address`,
`c`.`id`,
`c`.`city_id`
FROM (`sobha_enquiry` a)
LEFT JOIN `sobha_enquiryzone` b
ON `b`.`enquiry_id` = `a`.`id`
LEFT JOIN `sobha_admin` c
ON `c`.`city_id` = `b`.`city_id`
WHERE `a`.`city_id` = '2'
GROUP BY `a`.`id`
ORDER BY `c`.`id` desc, `a`.`id` DESC
In you where clause you should provide either b or c because are using aliases and the columns city_id exists in both table which is confusing mysql
the problem lies in the WHERE clause, you need to specify what table will be search for city_id sice both of them contains the same column name. It could be either from sobha_enquiryzone or from sobha_admin.
SELECT a.NAME,
a.company,
a.city,
a.country,
a.phone,
a.type_of_enquiry,
b.enquiry_id,
a.hearaboutus,
a.email,
a.comments,
a.address,
c.id,
c.city_id
FROM sobha_enquiry a
LEFT JOIN sobha_enquiryzone b
ON b.enquiry_id = a.id
LEFT JOIN sobha_admin c
ON c.city_id = b.city_id
WHERE b.city_id = '2' -- or c.city_id = '2' (both will yield the same result)
GROUP BY a.id
ORDER BY id DESC, a.id DESC

Can we use OR in a mysql inner join

SELECT DISTINCT (
A.`id`
), A.`id` , A.`no` , B.amount, SUM( A.`outward` * A.`price` ) AS total, A.`outward_date`
FROM `outward` A
INNER JOIN franchisees B
INNER JOIN store C
INNER JOIN shoppe D
WHERE B.user_id = C.user_id
AND (C.pos_id = A.no)//(C.pos_id = A.no OR (D.id = A.no)
OR (D.id = A.no)
AND A.outward_date = '2012-02-10'
GROUP BY A.req_id
ORDER BY A.no ASC , A.`d` ASC , B.amount ASC
The problem in this query is that the SUM( A.outward * A.price ) AS total comes differently which is not related to
ouptut
id sum(outward * price)
12021030738-105 485.220000000000
1202104186-104 2504.410000000000
output displayed
12021030738-105 32557.33
1202104186-104 6307.86
i guess the problem is with the OR statement? can anyone find the issue with the query
Try below. enclose OR Conditions in a parenthesis:
SELECT DISTINCT (
A.`id`
), A.`id` , A.`no` , B.amount, SUM( A.`outward` * A.`price` ) AS total, A.`outward_date`
FROM `outward` A
INNER JOIN franchisees B
INNER JOIN store C
INNER JOIN shoppe D
WHERE B.user_id = C.user_id
AND ((C.pos_id = A.no) OR (D.id = A.no))
AND A.outward_date = '2012-02-10'
GROUP BY A.req_id
ORDER BY A.no ASC , A.`d` ASC , B.amount ASC

mysql, use two select and order by, new value

SELECT a.id, a.name, a.ad, c.name, c.phone, c.email,
(
SELECT b.id_user
FROM price_b b
WHERE b.id = a.id
ORDER BY b.date DESC
LIMIT 1
) AS f_user_id
FROM a_emtp a
LEFT JOIN customer c ON
c.id = f_user_id
WHERE a.show = "1"
Hi, why show this error: Unknown column 'f_user_id' in 'on clause'
Thanks
SELECT a.id, a.name, a.ad, c.name, c.phone, c.email,
(
SELECT b.id_user
as f_user_id
FROM price_b b
WHERE b.id = a.id
ORDER BY b.date DESC
LIMIT 1
)
FROM a_emtp a
LEFT JOIN customer c ON
c.id = f_user_id
WHERE a.show = "1"
You can reference labelled fields from subqueries inside outer queries. You could even drop the relabelling and just use "b.id_user".
Probably a comma after c.email was missing.
SELECT a.id, a.name, a.ad, c.name, c.phone, c.email,
(
SELECT b.id_user
FROM price_b b
WHERE b.id = a.id
ORDER BY b.date DESC
LIMIT 1
) AS f_user_id
FROM a_emtp a
LEFT JOIN customer c ON
c.id = f_user_id
WHERE a.show = "1"