Improve SQL with WHERE sub query - mysql

I have this SQL that works fine although a little slow. Ideally would like to have the extra sub query in the where clause something like this AND country_iso = "uk".
SELECT c.content_name, c.content_telephone, c.content_address1
(SELECT w.weblink_url FROM weblinks w WHERE w.content_id = c.content_id ORDER BY w.weblink_ordering, w.weblink_id ASC LIMIT 1)
AS content_url,
(SELECT country_name FROM countries WHERE country_id = c.country_id LIMIT 1)
AS country_name,
(SELECT country_iso FROM countries WHERE country_id = c.country_id LIMIT 1)
AS country_iso
FROM catcontents cc
LEFT JOIN content c ON c.content_id = cc.content_id
WHERE cc.category_id = 7
AND (SELECT country_iso FROM countries WHERE country_id = c.country_id LIMIT 1) = 'uk'
ORDER BY cc.catcontent_ordering ASC, c.content_name ASC

Would this query not suit your needs? (basically, use joins to get the country, instead of sub-selects.)
SELECT
c.content_name,
c.content_telephone,
c.content_address1,
countries.country_name,
countries.country_iso,
(
SELECT w.weblink_url
FROM weblinks w
WHERE w.content_id = c.content_id
ORDER BY w.weblink_ordering, w.weblink_id ASC
LIMIT 1
) content_url
FROM catcontents cc
LEFT JOIN content c ON c.content_id = cc.content_id
JOIN countries ON countries.country_id = c.country_id
WHERE cc.category_id = 7
AND countries.country_iso = 'uk'
ORDER BY cc.catcontent_ordering ASC, c.content_name ASC

SELECT c.content_name,
c.content_telephone,
c.content_address1,
w.weblink_url content_url,
country_name,
country_iso
FROM catcontents cc
LEFT JOIN content c ON c.content_id = cc.content_id
LEFT JOIN weblink_url w ON w.content_id = c.content_id
JOIN countries ON country_id = c.country_id
AND country_iso = 'uk'
WHERE cc.category_id = 7

Related

HOW TO ACCESS OTHER JOIN ID ON LEFT JOIN SUBQUERY

Hello i have this query and I need to access de artist_id inside subquery. but don't work.
SELECT g.name, SUM(l.month_start_value), GROUP_CONCAT(l.artist_id), GROUP_CONCAT(l.month_start_value)
FROM genre g
LEFT JOIN genre_artist ga ON g.id = ga.genre_id
LEFT JOIN artist a ON ga.artist_id = a.id
LEFT JOIN (SELECT artist_id,
(SELECT CAST(value as SIGNED)
FROM platform_information_artist
WHERE platform_information_id =
(SELECT id from platform_information WHERE platform = 'spotify' AND information = 'monthly_listeners')
and artist_id = a.id
AND DATE(date) >= DATE(NOW()) - INTERVAL 30 DAY
ORDER BY date ASC
LIMIT 1) as month_start_value
FROM platform_information_artist
GROUP BY platform_information_artist.artist_id) l ON a.id = l.artist_id
GROUP BY g.id
ORDER BY g.id ASC;
The line that's wrong is
and artist_id = a.id
Returns this error: [42S22][1054] Unknown column 'a.id' in 'where clause'
I don't know why I can't access to a.id inside subquery.
There's a number of ways to do this. The least change from your current query is to simply change a couple of references:
SELECT g.name AS genre,
SUM(l.month_start_value) AS SumStartValue,
GROUP_CONCAT(l.artist_id) AS ArtistIDs,
GROUP_CONCAT(l.month_start_value) AS MonthStartValues
FROM genre g
LEFT JOIN genre_artist AS ga
ON g.id = ga.genre_id
LEFT JOIN artist AS a
ON ga.artist_id = a.id
LEFT JOIN
( SELECT pia.artist_id,
( SELECT CAST(pia2.value as SIGNED)
FROM platform_information_artist AS pia2
WHERE pia2.platform_information_id = (SELECT id
FROM platform_information
WHERE platform = 'spotify'
AND information = 'monthly_listeners')
AND pia2.artist_id = pia.artist_id
-- ^^ Updated from `a.id` to `pia.artist_id`
AND DATE(pia2.date) >= DATE(NOW()) - INTERVAL 30 DAY
ORDER BY pia2.date ASC
LIMIT 1
) as month_start_value
FROM platform_information_artist AS pia
GROUP BY pia.artist_id
) l
ON a.id = l.artist_id
GROUP BY g.id
ORDER BY g.id ASC;
This version should work in most versions of MySQL.
If you are using newer versions of MySql though you have a couple more options, which are both more efficient than the above. The first is to use a lateral join:
SELECT g.name AS genre,
SUM(l.month_start_value) AS SumStartValue,
GROUP_CONCAT(a.id) AS ArtistIDs,
GROUP_CONCAT(l.month_start_value) AS MonthStartValues
FROM genre g
LEFT JOIN genre_artist AS ga
ON g.id = ga.genre_id
LEFT JOIN artist AS a
ON ga.artist_id = a.id
LEFT JOIN LATERAL
( SELECT CAST(pia.value as SIGNED) AS month_start_value
FROM platform_information_artist AS pia
INNER JOIN platform_information AS i
ON i.id = pia.platform_information_id
WHERE i.platform = 'spotify'
AND i.information = 'monthly_listeners'
AND pia.artist_id = a.id
AND DATE(pia.date) >= DATE(NOW()) - INTERVAL 30 DAY
ORDER BY pia.date ASC
LIMIT 1
) AS l
ON 1= 1
GROUP BY g.id
ORDER BY g.id ASC;
The other is to use ROW_NUMBER() rather than limit to select one value per artist:
SELECT g.name AS genre,
SUM(l.month_start_value) AS SumStartValue,
GROUP_CONCAT(l.artist_id) AS ArtistIDs,
GROUP_CONCAT(l.month_start_value) AS MonthStartValues
FROM genre g
LEFT JOIN genre_artist AS ga
ON g.id = ga.genre_id
LEFT JOIN artist AS a
ON ga.artist_id = a.id
LEFT JOIN
( SELECT pia.artist_id,
CAST(pia.value as SIGNED) AS month_start_value,
ROW_NUMBER() OVER(PARTITION BY pia.artist_id ORDER BY pia.Date) AS RowNum
FROM platform_information_artist AS pia
INNER JOIN platform_information AS i
ON i.id = pia.platform_information_id
WHERE i.platform = 'spotify'
AND i.information = 'monthly_listeners'
AND DATE(pia.date) >= DATE(NOW()) - INTERVAL 30 DAY
) AS l
ON l.artist_id = a.id
AND l.RowNum = 1
GROUP BY g.id
ORDER BY g.id ASC;
Examples of all 3 on db<>fiddle

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 Multiple JOIN with most resent timestamp from one

I have problem that I hope someone can help me with.
SELECT a.country_name, s.state_name, c.city_id,
LEAST (c.next_1, c.next_2, c.next_3) AS next_visit,
MAX(v.visit_time) AS last_visit
FROM city c
INNER JOIN country a ON a.id = c.country
INNER JOIN state s ON s.id = c.state
INNER JOIN visit_log v ON CONCAT(c.country, c.state, c.city_id) = CONCAT(v.country, v.state, v.city_id)
GROUP BY CONCAT(v.country, v.state, v.city_id)
ORDER BY a.id ASC, s.id ASC, c.city_id
My main problem now is that I can't get the col_1 and col_2 from the visit_log corresponding with MAX(visit_log)
SQLfiddle
You can add the "latest" requirement to the join condition:
SELECT *
FROM city c
JOIN country a
ON a.id = c.country
JOIN state s
ON s.id = c.state
JOIN visit_log v
ON v.country = c.country
AND v.state = c.state
AND v.city_id = c.city_id
AND visit_time =
(
SELECT MAX(visit_time)
FROM visit_log v2
WHERE v2.country = c.country
AND v2.state = c.state
AND v2.city_id = c.city_id
)
You can find many other approaches in the greatest-n-per-group+mysql tag. For optimal speed you'd use an approach using variables.
You can try this:-
SELECT C.NAME, S.NAME, CN.ID, NV.Next_visit_1, VL.visited
FROM COUNTRY C INNER JOIN next_visit NV ON C.ID = NV.Country
INNER JOIN STATE S ON NV.State = S.ID
JOIN CITY
INNER JOIN visitor_log VL ON CONCAT(NV.country, NV.state, NV.city) = CONCAT(VL.country, VL.state, VL.city)

mysql order by multiple conditions

I have sql in below, that want to make multiple ORDER BY.
SELECT
r.*,
s.uzunluq,
b.id,
au.status_id as aksessuar_status,
au.aksessuar_id,
au.aksessuar
FROM
seksiya s,
result r
LEFT JOIN bosh_seksiya_aksessuar b
ON
b.bosh_seksiya = r.model AND
b.ERK = :ses
LEFT JOIN aksessuar_up au
ON au.model_id = r.res_id AND
au.user_id = :user_id AND
au.status_id = 9
WHERE
r.user_id = :user_id AND
r.model=s.seksiya AND
s.erk = :ses AND
r.status_id IN (1,2,3,4,5)
ORDER BY
r.res_id
I think to write php PDO is not important for you, guys, cause my question only about with this sql. This sql works very good, I just want to add extra function. So, look to this column: r.status_id IN (1,2,3,4,5)
I have given Order BY r.res_id
MY question:
I want to use multiple ORDER for each status_id
HOW to order:
ORDER BY r.res_id DESC WHERE r.status_id IN (1,2)
AND
ORDER BY r.res_id WHERE r.status_id IN (3,4,5)
in this sql?
ORDER BY IF(r.status_id IN (1,2), r.res_id, NULL) DESC, r.res_id
A recordset sorted with this ORDER BY clause will first display all records with r.status_id IN (1,2) (since NULL values come last in a descending ordering), themselves sorted in descending order of r.res_id; followed by all other values sorted by r.res_id in ascending order.
you can use union syntax:
SELECT * FROM ((SELECT
r.*,
s.uzunluq,
b.id,
au.status_id as aksessuar_status,
au.aksessuar_id,
au.aksessuar,
1 as query_order
FROM
seksiya s,
result r
LEFT JOIN bosh_seksiya_aksessuar b
ON
b.bosh_seksiya = r.model AND
b.ERK = :ses
LEFT JOIN aksessuar_up au
ON au.model_id = r.res_id AND
au.user_id = :user_id AND
au.status_id = 9
WHERE
r.user_id = :user_id AND
r.model=s.seksiya AND
s.erk = :ses AND
r.status_id IN (1,2)
ORDER BY
r.res_id DESC) as table1
UNION
(SELECT
r.*,
s.uzunluq,
b.id,
au.status_id as aksessuar_status,
au.aksessuar_id,
au.aksessuar,
0 as query_order
FROM
seksiya s,
result r
LEFT JOIN bosh_seksiya_aksessuar b
ON
b.bosh_seksiya = r.model AND
b.ERK = :ses
LEFT JOIN aksessuar_up au
ON au.model_id = r.res_id AND
au.user_id = :user_id AND
au.status_id = 9
WHERE
r.user_id = :user_id AND
r.model=s.seksiya AND
s.erk = :ses AND
r.status_id IN (3,4,5)
ORDER BY
r.res_id) as table2) sa table3 ORDER BY query_order

Order By not working on MySql

Hi Order By not working on MySql
the code is as follows,
select * from School where School.type = 'HighSchool'
order by (select locations.name from locations inner join School_locations
on locations.id = School_locations.location_id where
School_locations.School_id = School.id and locations.location_country = 'US' limit 1)
and the output is displaying same for both ascending as well as descending how to solve this problem
I don't think you'd need to do the subquery:
SELECT s.*
FROM School s
INNER JOIN School_locations sl ON (s.id = sl.School_id)
INNER JOIN locations l ON (l.id = sl.location_id)
WHERE l.location_country = 'US' AND s.type = 'High school'
ORDER BY l.name
select school.* from school
inner join school_locations on school_locations.schoolid = school.school_id
inner join locations on locations.location.id = school_locations.locationid
where
locations.location_country = 'US' and school.type = 'HighSchool'
order by
locations.name
limit 1
you can use this query
select School.* from School inner join School_locations
on School_locations.School_id = School.id
inner join locations
on locations.id = School_locations.location_id
where locations.location_country = 'US' and School.type = 'HighSchool'
order by locations.name limit 1