MySQL Inline View "unknown fields" - mysql

I am presently in a DBS course and I am working on an inline view:
SELECT userId, firstname, lastname, gender
FROM
(SELECT COUNT(dvdId) dvdId, u.userId
FROM userDVD
JOIN users u ON userDVD.userId = u.userId
GROUP BY userId) as T
WHERE gender = 'F';
When I run the Query it returns the error unknown column in field list. If I try to specify
u.firstname, u.lastname, u.gender
I return the same error. Any thoughts?

SELECT T.userId, T.firstname, T.lastname, T.gender
FROM (
SELECT users.userId, users.firstname, users.lastname, users.gender
FROM userDVD
JOIN users ON userDVD.userId = users.userId
WHERE gender = 'F' GROUP BY userId
) as T;
I worked through it and it turns out I didn't realize that because I had to alias my inline view that I needed to specify it in the original select statement. This concept was sadly not covered in my course. I appreciate those who gave helpful tips versus rude comments. So, Thank you Drew and Matt

Drew is right that you have a derived table T, but I'll attempt to add some details which might be useful to you as you are in a course.
you have a derived table T
an outer query (first select)
an inner query (second select in brackets)
the inner query uses a table alias of u for users.
the final columns of the inner query are dvdId which is a count of the dvdIds in userDvd table and userId
Because an outer query is constrained to use the final recordset of the inner query only those 2 columns are available to be selected and seeing they don't include firstname, lastname, or gender you are recieving the error.
If you want to keep your query the same but to use those columns you could join your derived table back to the user table and get the columns you desire. Such as this:
SELECT userId, firstname, lastname, gender
FROM
(SELECT COUNT(dvdId) dvdId, u.userId
FROM userDVD
JOIN users u ON userDVD.userId = u.userId
GROUP BY userId) as T
INNER JOIN users u2
ON t.user_id = u2.user_id
WHERE gender = 'F';
That technique to join back to the original table is great for when you have to optimize an aggregation that is on a large table or look for duplicates or something like that. However in your case you really just need a single query aggregating on all of the columns you want in the result set such as this:
SELECT
u.userId, u.firstname, u.lastname, u.gender, COUNT(d.dvdId) as DVDCount
FROM
userDVD d
JOIN users u
ON d.userId = u.userId
WHERE u.gender = 'F'
GROUP BY
u.userId, u.firstname, u.lastname, u.gender
;

Related

Performance issue with my MYSQL query with subqueries

I have a huge performance issue with my below query I figured that what is the leakage but don't know how to go around it.
the issue is that inner selects go through all the records in tables which is like 200K and then try to select and apply the filter to it so the whole data get selected in the first round.
SELECT *
FROM (
SELECT
( SELECT GROUP_CONCAT(tp.login) login
FROM tp
WHERE tp.user_id = ue.user_id ) login,
u.email as email,
ue.fname as name
FROM user_extra ue
LEFT JOIN users u ON u.id = ue.user_id
) t
WHERE
email like '%sradesign.net#gmail.com%'
OR fname like '%test%'
OR login like '%461988%
Test does I have not made a mistake:
SELECT GROUP_CONCAT(tp.login) login,
u.email as email,
ue.fname as name
FROM user_extra ue
JOIN users u ON u.id = ue.user_id -- I doubt in LEFT
JOIN tp ON tp.user_id = ue.user_id
GROUP BY ue.fname,
u.email
-- , ue.user_id -- maybe needed, but I doubt
HAVING email like '%sradesign.net#gmail.com%'
OR fname like '%test%'
OR login like '%461988%
This is intermediate variant - it must be divided on two (or maybe three - depends on your data) separate queries combined with UNION later.
sometimes it happens that one user doesn't have any login(there is no record for him in tp table) but I want to show him if the customer search term matched in the email of fname how to do this? – Ali Mahmoudi
In such case this table must be joined using outer joining (LEFT JOIN). – Akina
with left join again become like before performance issue the query run for 200 Second – Ali Mahmoudi
As I have said above - this is intermediate variant. If it is correct then let's divide taking into account possible absence in tp:
SELECT GROUP_CONCAT(tp.login) login,
u.email as email,
ue.fname as name
FROM user_extra ue
JOIN users u ON u.id = ue.user_id -- I doubt in LEFT
LEFT JOIN tp ON tp.user_id = ue.user_id
WHERE u.email like '%sradesign.net#gmail.com%'
GROUP BY ue.fname,
u.email
UNION ALL
SELECT GROUP_CONCAT(tp.login) login,
u.email as email,
ue.fname as name
FROM user_extra ue
JOIN users u ON u.id = ue.user_id -- I doubt in LEFT
LEFT JOIN tp ON tp.user_id = ue.user_id
WHERE ue.fname like '%test%'
GROUP BY ue.fname,
u.email
UNION ALL
SELECT GROUP_CONCAT(tp.login) login,
u.email as email,
ue.fname as name
FROM user_extra ue
JOIN users u ON u.id = ue.user_id -- I doubt in LEFT
JOIN tp ON tp.user_id = ue.user_id
GROUP BY ue.fname,
u.email
HAVING login like '%461988%
Test its output for correctness.
If it will produce duplicates then replace UNION ALL with UNION DISTINCT.
You seem to have an extra layer of SELECTs. This causes an extra temp table that contains a huge amount of data.
Unnecessary use of LEFT adds confusion and may prevent some optimizations.
Leading wildcards prevent usage of index.
OR is hard to optimize. But...
A common trick for optimizing OR:
( SELECT ... WHERE email like '%sradesign.net#gmail.com%' )
UNION DISTINCT
( SELECT ... WHERE fname like '%test%' )
UNION DISTINCT
( SELECT ... WHERE login like '%461988% )
However, because of the JOINs, UNION may not be practical.
Consider using FULLTEXT instead of LIKE.

How to optimize this mysql query - %a% or

SELECT DISTINCT u.id AS userId,u.type AS userType
FROM User AS u,Personal AS p,Company AS c
WHERE (p.realName LIKE '%adf%' AND u.type=1 AND u.id=p.userId)
OR (c.name LIKE '%grge%' AND u.id=c.userId)
LIMIT 0 , 10000
You can write your query as:
SELECT DISTINCT u.id AS userId,u.type AS userType
FROM User AS u inner join Personal AS p on u.id=p.userId
inner join Company AS c on u.id=c.userId
where p.realName LIKE '%adf%' or c.name LIKE '%grge%'
LIMIT 0 , 10000
Try to avoid comma seperated JOINS
You appear to be doing a quite hideous cross join, and then selectively narrowing down the records in the WHERE clause.
It is probably better to do 2 queries and union the results together. Each query can do one proper join. It is still going to have to access one column using the LIKE, and with a leading wild card that is not going to be quick (it can't use indexes).
SELECT u.id AS userId,
u.type AS userType
FROM User AS u
INNER JOIN Personal AS p
ON u.id = p.userId
WHERE p.realName LIKE '%adf%'
AND u.type = 1
UNION
SELECT u.id AS userId,
u.type AS userType
FROM User AS u
INNER JOIN Company AS c
ON u.id=c.userId
WHERE c.name LIKE '%grge%'
LIMIT 0 , 10000

select a column corresponding to max value in two joined tables

I have two tables, say Users and Interviews. One user can have multiple interview records.
Users
-----
UserID
FirstName
LastName
Interviews
----------
InterviewID
UserID
DateOfInterview
I want to get only the latest interview records. Here's my query
select u.UserID, firstname, lastname, max(DateOfInterview) as latestDOI
from users u
left join interviews i
on u.UserID = i.UserID
GROUP BY u.UserID, firstname, lastname
ORDER BY max(DateOfInterview) DESC
How do I update the query to return the InterviewID as well (i.e. the one which corresponds to max(DateOfInterview))?
Instead of using an aggregate function in your select list, you can use an aggregate subquery in your WHERE clause:
select u.UserID, firstname, lastname, i.InterviewId, DateOfInterview as latestDOI
from users u
left join interviews i
on u.UserID = i.UserID
where i.UserId is null or i.DateOfInterview = (
select max(DateOfInterview)
from interviews i2
where i2.UserId = u.UserId
)
That does suppose that max(DateOfInterview) will be unique per user, but the question has no well-defined answer otherwise. Note that the main query is no longer an aggregate query, so the constraints of such queries do not apply.
There are other ways to approach the problem, and it is worthwhile to look into them because a correlated subquery such as I present can be a performance concern. For example, you could use an inline view to generate a table of the per-user latest interview dates, and use joins to that view to connect users with the ID of their latest interview:
select u.*, im.latestDOI, i2.InterviewId
from
users u
left join (
select UserID, max(DateOfInterview) as latestDOI
from interviews i
group by UserID
) im
on u.UserId = im.UserId
left join interviews i2
on im.UserId = i2.UserId and im.latestDOI = i2.DateOfInterview
There are other alternatives, too, some standard and others DB-specific.
Rewrite to use an OUTER APPLY when grabbing your interview, that way you can use order by rather than MAX
select u.UserID, firstname, lastname, LatestInterviewDetails.ID, LatestInterviewDetails.DateOfInterview as latestDOI
from users u
OUTER APPLY (SELECT TOP 1 Id, DateOfInterview
FROM interviews
WHERE interviews.UserID = u.UserId
ORDER BY interviews.DateOfInterview DESC
) as LatestInterviewDetails
Note: This is providing you are using Microsoft SQL Server

Cardinality violation on mysql query

Im getting an error on this query:
SELECT i.name, i.surname, (SELECT u.username FROM user u WHERE u.info_id IN (1,9,10,15,25,40,42,43,44)) as email FROM `userinfo` i WHERE i.id IN (1,9,10,15,25,40,42,43,44)
Error:
Cardinality violation: 1242 Subquery returns more than 1 row
I know this could be solved by using a JOIN statment but i have no idea how to solve that.
To clarify more my question, I have two tables, user and userinfo:
User
id info_id username
Userinfo
id name surname
The info_id row from user is related to the id of userinfo, so having a list of userinfo ids in this format (1,4,7,8,9) I want name and surname (from userinfo) and username from user that match the info_id
You need to specify the relationship between the two table in the correlated subquery,
SELECT i.name,
i.surname,
(
SELECT u.username
FROM user u
WHERE u.info_id = i.id) as email
FROM userinfo i
WHERE i.id IN (1,9,10,15,25,40,42,43,44)
and by using JOIN (which I preferred more)
SELECT i.name,
i.surname,
u.username as Email
FROM userinfo i
INNER JOIN user u
ON u.info_id = i.id
WHERE i.id IN (1,9,10,15,25,40,42,43,44)
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
You're only allowed to return one row in a field list. It seems like you want to group the results.
SELECT
i.name, i.surname,
GROUP_CONCAT(u.username) AS email
FROM
userinfo i
JOIN user u ON (i.id = u.info_id)
WHERE
i.id IN (1,9,10,15,25,40,42,43,44)
GROUP BY
i.id
SELECT i.name, i.surname, u.username as email
FROM `userinfo` i
INNER JOIN
`user` u ON i.info_id=u.id
WHERE i.id IN (1,9,10,15,25,40,42,43,44)
Learn to use joins, without them you are working wit both hands tied behind your back AND blindfolded. Joins are how to use the Relational part of Relational Databases.
Do you mean:
SELECT i.name, i.surname, (SELECT u.username FROM user u WHERE u.id IN (1,9,10,15,25,40,42,43,44)) as email
FROM `userinfo` i
WHERE i.id IN (1,9,10,15,25,40,42,43,44)

MYSQL JOIN syntax How to Join Three Tables

The following query does what I want. It returns all the resuls in the users table and then if there is a match in the details tble, returns the relevant data
users
id|username
details
id|userid|firstname|lastname
$sql = "SELECT u.*, d.*
FROM `users` u
LEFT JOIN `details` d on
u.id = d.userid
ORDER BY $strorder";
However, when I try to join an additonal table where I want to do the same thing--return all the results of the users table and if there is a match in the third table, return the relevant data (total followers of this user)--it only returns one record.
3rd table
follow
id|followerid|followedid
$sql = "SELECT u.*, d.*, COUNT(f.id)
FROM `users` u
LEFT JOIN `details` d on
u.id = d.userid
LEFT JOIN `follow` f on
u.id = f.followedid
ORDER BY $strorder";
Can anyone see what I am doing wrong? Thanks in advance for any suggestions.
Many thanks.
Try to avoid * to select fields, it will be clearer to group your datas (even if mysql is quite permissive with groupings).
When you have an aggregate function (like COUNT, SUM), the other "non aggregated" requested fields should be in a GROUP BY clause.
Mysql don't force you to GROUP BY all the fields, but... I think it's quite a good habit to be "as ANSI as possible" (usefull when you use another DBMS)
SELECT u.id, u.username, d.firstname, d.lastname, count(*) as numberfollowers
FROM user u
LEFT JOIN details d on u.id = d.userid
LEFT JOIN follow f on u.id = f.followedid
GROUP BY u.id, u.username, d.firstname, d.lastname --or just GROUP BY u.id with Mysql
ORDER BY count(*) desc
COUNT being an aggregate function, when selected with other columns, requires you to group your results by those other columns in the select list.
You should rewrite your query with columns that you want to select from users and details and group by those columns.