I am quite an amateur with SQL (I am a teacher rather than programmer) and have written some code which worked in SQLITE but does not work in MYSQL. I have a table called 'scores' of student names, exercises and scores. As well as returning the completed exercises, I also wish to return those which are not completed. For this I used CROSS JOIN to generate all combinations of student and exercise. In SQLITE this returned null values for students that had not completed a particular exercise, which is useful.
Using this code in MYSQL, it complains that "Every derived table must have its own alias"
I understand this means that a subquery must have an 'AS ...' term. However, no matter where I add it, I still receive that error. I have tried online SQL 'validators' to help me, but still no success.
Can anybody help improve my code please ? Thank you
SELECT othertable.name , othertable.exercise, scores.score
from ((select distinct name from scores)
CROSS JOIN (select distinct exercise from scores)) AS othertable
left join scores
on(scores.name = othertable.name AND scores.exercise = othertable.exercise )
order by othertable.name, othertable.exercise
The problem with your code is what the error says, in MySql you must set an alias for each subquery of your query.
But instead of aliasing the 2 cross joined subqueries and the result of their join, drop the parentheses of the subqueries and use 3 aliases:
SELECT n.name, e.exercise, s.score
FROM (SELECT DISTINCT name FROM scores) AS n
CROSS JOIN (SELECT DISTINCT exercise FROM scores) AS e
LEFT JOIN scores AS s
ON s.name = n.name AND s.exercise = e.exercise
ORDER BY n.name, e.exercise
Related
I had to get the town name and state from database which deployed the first reciever(a device to sense sharks). I used the following query with a subquery computing MIN of deployed dates:
SELECT t.town,t.state,r.deployed FROM receiver as r
inner join township as t
on r.location=t.tid
where deployed=(select min(deployed) from receiver);
I got following result which is correct:
Result from query
I tried to do the same thing by using having clause i.e without the subquery as following:
SELECT t.town,t.state,r.deployed FROM receiver as r
inner join township as t
on r.location=t.tid
having r.deployed=min(deployed);
But I got no rows from this query. My question is whats the problem with second query? Am I not using the min function properly in the query? What's the right way to write this query without subquery so that I can get the result just as I got from the former query?
If you don't care about ties, we can use a limit query here:
SELECT t.town, t.state, r.deployed
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
ORDER BY r.deployed
LIMIT 1;
If you do want all ties, then RANK() is another option, but that would require a subquery:
WITH cte AS (
SELECT t.town, t.state, r.deployed, RANK() OVER (ORDER BY r.deployed) rnk
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
)
SELECT town, state, deployed
FROM cte
WHERE rnk = 1;
Note that some other databases (besides MySQL) do support a QUALIFY clause, which actually would let us use RANK() without a formal subquery:
SELECT t.town, t.state, r.deployed
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
QUALIFY RANK() OVER (ORDER BY r.deployed) = 1;
You cannot use the field deployed both as an aggregate MIN(deployed) AND a non-aggregate r.deployed at the same time. The first query works because the subquery searches and finds ONE value, and then the main query compares ALL values, one by one, to that one found value to find the right one. These are two separate actions, both (in theory) going through their individual entire datasets. You cannot combine these two very different actions in one query without subquery.
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.
This is my code :
SELECT *
FROM Event_list
WHERE interest in
(
SELECT Interest_name
from Interest
where Interest_id in
(
SELECT Interest_id
FROM `User's Interests`
where P_id=Pid and is_canceled=0
)
)
order by count(Eid) desc
I don't use any GROUP BY clause but still only get one row. when removing the ORDER BY clause I get all the correct rows (but not in the right order).
I'm trying to return a view (named Event_list) sorted by most common Eid (Event id), but I want to see every row without any grouping.
COUNT() is a group function, so using it will automatically result in grouping of rows. This is why you get only one row in your result when you use it in your ORDER BY clause.
Unfortunately, it's not clear what you're trying to do, so I can't tell you how to rewrite your query to get your desired results.
I suspect the query you want is more like this:
SELECT el.*,
(select count(*)
from interest i join
UserInterests ui
on ui.is_canceled = 0 and ui.p_id = i.id
where el.interest = i.interest_name
) as cnt
FROM Event_list el
ORDER BY cnt desc;
It is a bit hard to tell without sample data and a better formed query. Some notes:
Don't use special characters in table and column names. Having to escape the names merely leads to queries that are harder to read, write, and understand.
Qualify column names, so you know what tables columns come from.
Use table aliases -- so queries are easier to write and to read.
The WHERE clause only does filtering. Your description of the problem doesn't seem to involve filtering, only ordering.
Any time you use an aggregation function, the query automatically becomes an aggregation query. Without a group by, exactly one row is returned.
Give foreign keys the same names as primary keys, where possible.
You may try:
SELECT L.* , C.Cnt
FROM Event_list L
LEFT JOIN (
SELECT E.EID, COUNT(*) AS Cnt
FROM Event_List E
JOIN Interest I
ON E.Interest = I.Interest_name
JOIN `User's Interests` U
ON U.Interest_id = I.Insert_Id
Where U.P_id=Pid and U.is_canceled=0
GROUP BY E.EID
) C
ON E.Eid = C.Eid
Order By Cnt DESC
I don't have the tables to test so you may want to correct column names and other conditions. Just provide you the idea.
I have to get the names of the Departments and the number of Employees in it. Test is my schema.
So I come up with two queries that give me the same result -
First
SELECT Department.Departmentname,
(
SELECT COUNT(*)
FROM test.Employee
WHERE Employee.Departmentid = Department.idDepartment
) AS NumberOfEmployees
FROM test.Department;
Second
SELECT Department.Departmentname AS NAme,COUNT(Employee.idEmployee) AS Employee_COUNT
FROM test.Department
LEFT JOIN test.Employee
ON Employee.Departmentid = Department.idDepartment
GROUP BY Employee.Departmentid ;
Which of the two is the best and efficient way to get the required result? Any other solution is welcome.
Please explain why a particular solution is better
My preference for expressing the logic is the second query, which I would write as:
SELECT d.Departmentname AS Name, COUNT(e.idEmployee) AS Employee_COUNT
FROM test.Department d LEFT JOIN
test.Employee e
ON e.Departmentid = d.idDepartment
GROUP BY d.Departmentname;
Note the use of table aliases and the fact that the GROUP BY uses the same columns as the SELECT. However, in MySQL, this query will not use an index on DepartmentName for the group by. That means that the GROUP BY is doing a file sort, a relatively expensive operation.
When you write the query like this:
SELECT d.Departmentname,
(SELECT COUNT(*)
FROM test.Employee e
WHERE e.Departmentid = d.idDepartment
) AS NumberOfEmployees
FROM test.Department d;
No explicit group by is needed. With an index on Employee(DepartmentId) this will use the index for the count(*), so this version would normally perform better in MySQL.
The difference in performance is probably negligible until you start having thousands or ten of thousands of rows.
I am having a small issue with a simple SQL statement.
I need to find out if I have 7 copies of a movie in the movie table and 6 people have rented it out, I need to see that I only have 1 copy left (I need to do this all through the SQL query). I know normally I would do it using PHP and just takeaway the number hired from the total number of copies, but sadly my college wants me to do it the other way.
SELECT *,
COUNT(distinct hire.movie_id) AS num_orders
FROM `movie`
INNER JOIN hire ON hire.movie_id = movie.id
WHERE num_orders < movie.no_copies;
When I run this I get the following issue #1054 - Unknown column 'num_orders' in 'where clause'.
You can't use an alias in a where predicate - you either need to repeat the predicate, wrap the query in a derived table before filtering on the alias, or you can also use HAVING as per below (in MySql, at least).
I don't see the need for DISTINCT h.movie_id (since you want to count the rentals? - possibly DISTINCT h.hireid ?), and it seems you will need to group by the movies to count the number of rentals.
How about:
SELECT m.id, m.no_copies, COUNT(h.movie_id) AS num_rentals
FROM `movie` m
INNER JOIN hire h ON h.movie_id = m.id
GROUP BY m.id, m.no_copies
HAVING num_rentals < m.no_copies;