I have a MySQL query which I want to execute to see who is the employee with the best skill X in a company I work for. To do this I randomly pick a company from my cv_profile (skill_cv_test) and find all users who work there for the same employer. And then I randomly choose a skill I have.
The result should either be zero or a list.
But when testing with PHPMyAdmin I get results where I don't see any row, but the status says there is at least one row.
Here's an example of the message I get: https://imgur.com/bVMH716
I have been trying different structures, even "walling" the query with another query, different joins.
SELECT
DISTINCT(sv.usr_id),
u.first_name AS fn,
u.last_name AS ln,
c.name AS company,
s.name AS skill
FROM
(
SELECT
MAX(last_change) as date,
id,
usr_id,
skill_id
FROM skill_valuations
GROUP BY usr_id, skill_id
ORDER BY date
) sv
LEFT JOIN skill_valuations skv ON skv.last_change = sv.date
INNER JOIN
(
SELECT
DISTINCT(skct.comp_id),
skct.usr_id AS usr_id,
skct.category
FROM skill_cv_test skct
WHERE skct.end_date IS NULL AND skct.comp_id IN (SELECT comp_id FROM (SELECT comp_id FROM skill_cv_test WHERE usr_id = 1 ORDER BY RAND() LIMIT 1) x)
) uqv ON uqv.usr_id = sv.usr_id
INNER JOIN
(
SELECT skill_id
FROM usr_skills
WHERE usr_id = $uid
ORDER BY RAND()
LIMIT 1
) usq ON usq.skill_id = sv.skill_id
LEFT JOIN companies c ON c.id = uqv.comp_id
LEFT JOIN skills s ON s.id = sv.skill_id
LEFT JOIN users u ON u.id = sv.usr_id
As mentioned before, I expect either no results or a result of at least one row.
Related
[DB Table]
SELECT b.first_name, b.last_name, a.pod_name, a.category, c.user_id,
SUM(IF(QUARTER(CURDATE())-1 OR (QUARTER(CURDATE())-2) AND a.user_id, 1, 0)) AS flag FROM kudos a
INNER JOIN users b ON a.user_id = b.id INNER JOIN users_groups c ON a.user_id = c.user_id
INNER JOIN groups d ON c.group_id = d.id WHERE a.group_name = 'G2' AND d.id IN (7,8,9,11,12,13,14,15,16,17,21,22,23,24,25,26,27,28)
AND QUARTER(CURDATE())-1 = a.quarter ORDER BY a.final_score+0 DESC
I need to get the user_ids of those users which are both in quarter 1 and 2 from table.
Tried above query but failed to get expected results.
Can someone please guide me on this?
if you only need user_id then you can do this :
select user_id
from tablename
where quarter in (1,2)
group by user_id
having count(distinct quarter) = 2
another way is to use window function, assuming you have one user id in each quarter:
select * from (
select * , count(*) over (partition by user_id) cn
from tablename
where quarter in (1,2)
) t where cn = 2
I am trying to make a query to fetch the newest car for each user:
select * from users
left join
(select cars.* from cars
where cars.userid=users.userid
order by cars.year desc limit 1) as cars
on cars.userid=users.userid
It looks like it says Unknown column "users.userid" in where clause
I tried to remove cars.userid=users.userid part, but then it only fetches 1 newest car, and sticks it on to each user.
Is there any way to accomplish what I'm after? thanks!!
For this purpose, I usually use row_number():
select *
from users u left join
(select c.* , row_number() over (partition by c.userid order by c.year desc) as seqnum
from cars c
) c
on c.userid = u.userid and c.seqnum = 1;
One option is to filter the left join with a subquery:
select * -- better enumerate the columns here
from users u
left join cars c
on c.userid = u.userid
and c.year = (select max(c1.year) from cars c1 where c1.userid = c.userid)
For performance, consider an index on car(userid, year).
Note that this might return multiple cars per user if you have duplicate (userid, year) in cars. It would be better to have a real date rather than just the year.
Maybe there are better and more efficient way to query this. Here is my solution;
select users.userid, cars.*
from users
left join cars on cars.userid = users.userid
join (SELECT userid, MAX(year) AS maxDate
FROM cars
GROUP BY userid) as sub on cars.year = sub.maxDate;
One table is Users with id and email columns.
Another table is Payments with id, created_at, user_id and foo columns.
User has many Payments.
I need a query that returns each user's email, his last payment date and this last payment's foo value. How do I do that? What I have now is:
SELECT users.email, MAX(payments.created_at), payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
GROUP BY users.id
This is wrong, because foo value does not necessarily belong to user's most recent payment.
Try this :
select users.email,foo,create_at
from users
left join(
select a.* from payments a
inner join (
select id,user_id,max(create_at)
from payments
group by id,user_id
)b on a.id = b.id
) payments on users.id = payments.user_id
If users has no payment yet, then foo and create_at would return NULL. if you want to exclude users who has no payment, then use INNER JOIN.
One approach would be to use a MySQL version of rank over partition and then select only those rows with rank = 1:
select tt.email,tt.created_at,tt.foo from (
select t.*,
case when #cur_id = t.id then #r:=#r+1 else #r:=1 end as rank,
#cur_id := t.id
from (
SELECT users.id,users.email, payments.created_at, payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
order by users.id asc,payments.created_at desc
) t
JOIN (select #cur_id:=-1,#r:=0) r
) tt
where tt.rank =1;
This would save hitting the payments table twice. Could be slower though. Depends on your data!
I am having trouble trying to create a query to:
Select all the students who have not completed all peer review's for a particular week.
background: Each week, every student must peer review their peers in the same group.
Each group can be a different size, which is the problem I am having.
this is my current test data:
Table 1: peer review table
Table 2: student table.
This is my inital query, groups all the students based on the amount of peer review's they've made. I now need to to check if the count(*) is less than the size of the group for each student :
SELECT *
FROM peerreview
RIGHT JOIN student
ON student. studentID = peerreview.reviewer
WHERE week = 11
GROUP BY studentID
HAVING Count(*) < ????
Following query will return the student which has reviewed all the students in same group.
SELECT a.reviewer,
a.groupid
FROM (SELECT student2.studentID AS reviewer,
student1.groupid,
Count(*) AS cnt
FROM student student1
INNER JOIN peerreview
ON student1.studentID = peerreview.reviewee
INNER JOIN STUDENT STUDENT2
ON student2.studentID = peerreview.reviewer
WHERE student2.groupid = student2.groupid
AND peerreview.week = 11
GROUP BY student1.groupid,
student2.studentID) a
INNER JOIN (SELECT groupid,
Count(*) - 1 AS cnt
FROM student
GROUP BY groupid) b
ON a.groupid = b.groupid
AND a.cnt = b.cnt
See SqlFiddle
Select S.StudentId As Reviewer
, S1.StudentId As StudentYetToBeReviewed
, Weeks.WeekNum
From Student As S
Join Student As S1
On S1.GroupId = S.GroupId
And S1.StudentId <> S.StudentId
Cross Join (
Select 7 As WeekNum
Union All Select 11
) As Weeks
Where Not Exists (
Select 1
From PeerReview As P1
Where P1.reviewee = S1.StudentId
And P1.Week = Weeks.WeekNum
)
Order By WeekNum, reviewer
This provides you a list, by week, of the reviewer and the person they need to review. In the real solution, you would want to replace the Cross Join of weeks with a distinct list of weeks in which reviews should happen.
SQL Fiddle version
select distinct s1.*
from student s1 inner join student s2 on s1.groupId = s2.groupeId
left join peerreview pr on pr.revieweer = s1.studentId
and pr.reviewee = s2.studentId
where pr.Week = ? and pr.revieweer is null and s1.studentId <> s2.studentId
I'd like to select a new column named sliced (value can be 1/0 or true/false it doesn't matter) if area of the current row equals MAX(SUM(c.area)), that is flag the row with highest aggregate value:
SELECT p.name AS name, SUM(c.area) AS area
FROM City AS c
INNER JOIN Province AS p ON c.province_id = p.id
INNER JOIN Region AS r ON p.region_id = r.id
WHERE r.id = ?
GROUP BY p.id
ORDER BY p.name ASC
I've tried adding to the selection area = MAX(area) AS sliced or even area = SUM(MAX(c.area)) AS sliced but i'm getting a syntax error. I've to admit i'm not so good in SQL. Thank you.
As I understand your question, this should do it. Creates a pseudo-column which returns 1 when the area is the same as max(area) without any conditions to restrict your selection.
SELECT name
, area
, case area when max_area then 1 else 0 end as sliced
FROM ( SELECT name
, area
, max(area) over (partition by 1) as max_area
FROM ( SELECT p.name AS name
, SUM(c.area) AS area
FROM City AS c
INNER JOIN Province AS p ON c.province_id = p.id
INNER JOIN Region AS r ON p.region_id = r.id
WHERE r.id = ?
GROUP BY p.id
ORDER BY p.name ASC )
)
EDIT As #Glide says you can't perform nested aggregation so sum(max(area)) won't work and you need to perform these operations one query at a time.
Here's a way to do it with just one group by:
set #row := 0;
select name, area, sliced
from (
select name, area, (#row := #row + 1) = 1 as sliced
from (
SELECT p.name, SUM(c.area) AS area
FROM City AS c
INNER JOIN Province AS p ON c.province_id = p.id
INNER JOIN Region AS r ON p.region_id = r.id
WHERE r.id = ?
GROUP BY 1
ORDER BY 2 desc) t1
) t2
order by 1;
The inner query (t1) does the group by and orders by total area largest first.
The next query (t2) gives the first row a value of true for column sliced, all other rows false.
The outer query orders the rows in the way you want - by name.
Since there's only one table scan and group by, this should be very efficient.
As comments have mentioned, you'd have to check all the values against another query. This is normal practice in SQL.
SELECT
p.name AS name,
SUM(c.area) AS area,
CASE WHEN SUM(c.area) = (SELECT MAX(area) FROM <repeat your query here>) THEN 1 ELSE 0 END
FROM
City AS c
INNER JOIN
Province AS p
ON c.province_id = p.id
INNER JOIN
Region AS r
ON p.region_id = r.id
WHERE
r.id = ?
GROUP BY
p.id
ORDER BY
p.name ASC
The biggest downside to this is that you've had to repeat the code, which is just messy and a maintenance headache.
The alternative is to insert all the data into a temporary table, with the sliced field being 0 for all records. Then update that table, setting sliced to 1 for the record(s) with the highest area.