The following SQL zoo query is apparently wrong - mysql

I have a problem with the following exercise query on this page http://sqlzoo.net/3.htm :
4d. List the 1978 films by order of cast list size.
I'm trying to do the query:
SELECT DISTINCT(m1.title), COUNT(c1.actorid)
FROM ((movie AS m1 JOIN casting AS c1 ON m1.id=c1.movieid) JOIN actor AS a1 ON a1.id=c1.actorid)
WHERE m1.yr=1978
GROUP BY m1.title
ORDER BY COUNT(c1.actorid) DESC
But it doesn't give the right answer, and I don't know why. Am I wrong?

This should do it:
select m.title, count(c.actorid)
from movie m
join casting c on c.movieid = m.id
where m.yr = 1978
group by m.title
order by 2 desc
As you don't need any information from the actors, you don't need to include that table in the join.
Btw: your usage of distinct shows two misunderstandings:
distinct is not a function. It always operates on all columns of the result
when you do a group by there is no need to also to a distinct (as the group by will already return only the distinct values of the grouped columns)

Related

MySQL: Optimizing Sub-queries

I have this query I need to optimize further since it requires too much cpu time and I can't seem to find any other way to write it more efficiently. Is there another way to write this without altering the tables?
SELECT category, b.fruit_name, u.name
, r.count_vote, r.text_c
FROM Fruits b, Customers u
, Categories c
, (SELECT * FROM
(SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r
WHERE b.fruit_id = r.fruit_id
AND u.customer_id = r.customer_id
AND category = "Fruits";
This is your query re-written with explicit joins:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN
(
SELECT * FROM
(
SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r on r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
CROSS JOIN Categories c
WHERE c.category = 'Fruits';
(I am guessing here that the category column belongs to the categories table.)
There are some parts that look suspicious:
Why do you cross join the Categories table, when you don't even display a column of the table?
What is ORDER BY fruit_id, count_vote DESC, r_id supposed to do? Sub query results are considered unordered sets, so an ORDER BY is superfluous and can be ignored by the DBMS. What do you want to achieve here?
SELECT * FROM [ revues ] GROUP BY fruit_id is invalid. If you group by fruit_id, what count_vote and what r.text_c do you expect to get for the ID? You don't tell the DBMS (which would be something like MAX(count_vote) and MIN(r.text_c)for instance. MySQL should through an error, but silently replacescount_vote, r.text_cbyANY_VALUE(count_vote), ANY_VALUE(r.text_c)` instead. This means you get arbitrarily picked values for a fruit.
The answer hence to your question is: Don't try to speed it up, but fix it instead. (Maybe you want to place a new request showing the query and explaining what it is supposed to do, so people can help you with that.)
Your Categories table seems not joined/related to the others this produce a catesia product between all the rows
If you want distinct resut don't use group by but distint so you can avoid an unnecessary subquery
and you dont' need an order by on a subquery
SELECT category
, b.fruit_name
, u.name
, r.count_vote
, r.text_c
FROM Fruits b
INNER JOIN Customers u ON u.customer_id = r.customer_id
INNER JOIN Categories c ON ?????? /Your Categories table seems not joined/related to the others /
INNER JOIN (
SELECT distinct fruit_id, count_vote, text_c, customer_id
FROM Reviews
) r ON b.fruit_id = r.fruit_id
WHERE category = "Fruits";
for better reading you should use explicit join syntax and avoid old join syntax based on comma separated tables name and where condition
The next time you want help optimizing a query, please include the table/index structure, an indication of the cardinality of the indexes and the EXPLAIN plan for the query.
There appears to be absolutely no reason for a single sub-query here, let alone 2. Using sub-queries mostly prevents the DBMS optimizer from doing its job. So your biggest win will come from eliminating these sub-queries.
The CROSS JOIN creates a deliberate cartesian join - its also unclear if any attributes from this table are actually required for the result, if it is there to produce multiples of the same row in the output, or just an error.
The attribute category in the last line of your query is not attributed to any of the tables (but I suspect it comes from the categories table).
Further, your code uses a GROUP BY clause with no aggregation function. This will produce non-deterministic results and is a bug. Assuming that you are not exploiting a side-effect of that, the query can be re-written as:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN Reviews r
ON r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
ORDER BY r.fruit_id, count_vote DESC, r_id;
Since there are no predicates other than joins in your query, there is no scope for further optimization beyond ensuring there are indexes on the join predicates.
As all too frequently, the biggest benefit may come from simply asking the question of why you need to retrieve every single row in the tables in a single query.

Join MySQL Tables to Display Count

Firstly, I'm a beginner to MySQL and I'm still learning. I'm trying to join 2 tables to display a count. Primarily, I use 2 codes. One code to display names -
SELECT tag_logs.timestamp, People.Name FROM `tag_logs` INNER JOIN People WHERE tag_logs.tag_no = People.nametag
Another code to display count of names -
SELECT tag_logs.tag_no, COUNT(tag_logs.tag_no) FROM tag_logs GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I want to display Name and a count number, instead of a tag number and count. I attempted to join both tables by using the following code, however, I've had little luck -
SELECT People.Name FROM `tag_logs` INNER JOIN People WHERE tag_logs.tag_no = People.nametag AND COUNT(tag_logs.tag_no) FROM tag_logs GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I'm given an error when I try to call 'FROM tag_logs' a second time. Is there a way to work around this?
I aim to make this my final result, except I should be able to see names instead of numbers.
Two tables are joined using ON clause. You should learn joins.
SELECT People.Name ,COUNT(tag_logs.tag_no)
FROM `tag_logs`
INNER JOIN People ON tag_logs.tag_no = People.nametag
GROUP BY tag_logs.tag_no
HAVING COUNT(tag_no) >= 1
It should be
SELECT People.Name FROM `tag_logs`
INNER JOIN People on tag_logs.tag_no = People.nametag
GROUP BY tag_no HAVING COUNT(tag_no) >= 1
EDIT
SELECT People.Name, COUNT(tag_no) FROM `tag_logs`
INNER JOIN People on tag_logs.tag_no = People.nametag
GROUP BY tag_no HAVING COUNT(tag_no) >= 1
I believe the query that you want looks like this:
SELECT p.Name, COUNT(*)
FROM tag_logs tl INNER JOIN
People p
ON tl.tag_no = p.nametag
GROUP BY p.Name;
Notes:
COUNT(*) is shorter than COUNT(tl.tag_no) and they do the same thing.
GROUP BY clause now matches the SELECT. If you could have people with the same names, then add p.nametag to the GROUP BY. A version use only GROUP BY tl.tag_no is invalid SQL and should fail in most databases, because of the non-matching p.Name in the SELECT.
The HAVING clause (HAVING COUNT(tag_no) >= 1) is unnecessary, because the INNER JOIN requires at least one match and tag_no is never NULL (because it is used for the JOIN).
I introduced table aliases, so the query is easier to write and to read.

Count(*) an attribute from subquery

Question:
How would you display one field from an outer query and another from an inner subquery?
My Problem:
I need to display the names of actors AND how many movies each one has made (even if they haven't been in any) without any left or right joins.
Star(starnumb, starname)
MovStar(starnumb, mvnumb)
I previously displayed the information using a join;
SELECT starname, COUNT(movstar.starnumb) AS numMovies
The issue I have is the information for the amount of movies a star has acted in are inside the Movstar relation and the star's name is inside the Star relation.
SELECT starname, COUNT(*) AS numMovies
FROM star s
Where starnumb NOT IN
(SELECT movstar.starnumb
FROM movstar)
OR starnumb IN
(SELECT movstar.starnumb
FROM movstar)
GROUP BY starname
ORDER BY numMovies DESC;
The code runs, but I can't get the right information to display inside the Count field;
SELECT starname, COUNT(*) AS numMovies
You can correlate a subquery e.g. by comparing one of its columns with one of the columns from the outer query. And count() will give 0 if no matching records was found.
SELECT s.starname,
(SELECT count(*)
FROM movstat ms
WHERE ms.starnumb = s.startnumb) nummovies
FROM star s;
Why you don't want to use join?
SELECT s.starname, count(*) AS numMovies FROM star s LEFT JOIN movstar m ON m.starnumb = s.starnumb GROUP BY startname ORDER BY numMovies DESC
should get you what you ask for if I understood it correctly.

SQL query regarding nested query topic

Query that displays all "Father (Husband)" names in alphabetical order from Guardian table with total number of their "Wives" (use nested query to find count of wives).
enter image description here
You could use a self join and group by
select a.guardianName as husband_name, count(*) as count_of_wives
from my_table a
left join my_table b on b.husband_id = a.guardianId
where a.husband_id is null
group by a.guardianName

group by but still displaying all data?

i wonder how to using group by but still displaying full data? i just want to group it.
here i give an example of my table :
this is my query :
(SELECT dp.menu_paket,d.id_detail,t.no_meja,m.nama_menu,d.jumlah,t.status,t.nama_pegawai
FROM menu m
join detail_paket dp on dp.menu_paket=m.nama_menu
JOIN detail_transaksi d on m.id_menu = d.id_menu
join transaksi t on t.id_transaksi=d.id_transaksi where t.status='progress' and d.status_menu='progress' group by id_detail)
UNION
(SELECT dp.menu_paket,d.id_detail,t.no_meja,p.nama_paket,d.jumlah,t.status,t.nama_pegawai
FROM paket p
join detail_paket dp on dp.id_paket=p.id_paket
JOIN detail_transaksi d on d.id_paket=p.id_paket
join transaksi t on t.id_transaksi=d.id_transaksi where t.status='progress' and d.status_menu='progress' group by id_detail);
thanks..!
You can apply distinct to avoid same multiple records instead of group by. because group by is used when there is aggregate function is your query.
Distinct retrieves single row instead of multiple rows when two rows are totally same.
Try this
select distinct columnname from table name
union
select distinct columnname1 from table name
I think I see two issues.
1) GROUP BY is generally used when you want to group rows for an aggregate function like SUM. You may be looking for ORDER BY, which controls the order of the rows. You can specify multiple columns for ORDER BY to obtain a "grouping" effect. This is what you want if you just want the rows to be next to each other in the list.
2) UNION, at least in the databases I know of, removes duplicate rows. You want UNION ALL if you want to preserve all rows.
Edit:
In response to the poster's comment, you definitely want ORDER BY and maybe UNION ALL. It should be ORDER BY no_meja, id_transaksi. Try the following query and see if it gives you what you want:
SELECT * FROM
((SELECT dp.menu_paket,d.id_detail,t.no_meja,m.nama_menu,d.jumlah,t.status,t.nama_pegawai
FROM menu m
join detail_paket dp on dp.menu_paket=m.nama_menu
JOIN detail_transaksi d on m.id_menu = d.id_menu
join transaksi t on t.id_transaksi=d.id_transaksi
where t.status='progress' and d.status_menu='progress')
UNION ALL
(SELECT dp.menu_paket,d.id_detail,t.no_meja,p.nama_paket,d.jumlah,t.status,t.nama_pegawai
FROM paket p
join detail_paket dp on dp.id_paket=p.id_paket
JOIN detail_transaksi d on d.id_paket=p.id_paket
join transaksi t on t.id_transaksi=d.id_transaksi
where t.status='progress' and d.status_menu='progress')) x
ORDER BY x.no_meja, x.id_transaksi;