I want to run a query on MySql version 5.1.9 that returns me only top two (order by JoiningDate) of selected Dept.
For example, my data is like:
+-------+------------------------------------------+----------+------------+
| empid | title | Dept | JoiningDate|
+-------+------------------------------------------+----------+------------+
| 1 | Research and Development | 1 | 2015-08-06 |
| 2 | Consultant | 2 | 2015-08-06 |
| 3 | Medical Consultant | 3 | 2015-08-06 |
| 4 | Officer | 4 | 2015-08-06 |
| 5 | English Translator | 5 | 2015-08-06 |
| 6 | Teacher | 1 | 2015-08-01 |
| 7 | Physical Education | 2 | 2015-08-01 |
| 8 | Accountant | 3 | 2015-08-01 |
| 9 | Science Teacher | 4 | 2015-08-01 |
| 10 | Home Science | 5 | 2015-08-01 |
| 11 | Research Assistant | 1 | 2015-08-05 |
| 12 | Consultant | 2 | 2015-08-05 |
| 13 | Consultant HR | 3 | 2015-08-05 |
| 14 | Technical Lead | 4 | 2015-08-05 |
| 15 | Hindi Translator | 5 | 2015-08-05 |
| 16 | Urdu Teacher | 1 | 2015-08-02 |
| 17 | Physical Education | 2 | 2015-08-02 |
| 18 | Accountant | 3 | 2015-08-02 |
| 19 | Science | 4 | 2015-08-02 |
| 20 | Home Science | 5 | 2015-08-02 |
+-------+------------------------------------------+----------+------------+
I want the query to output the latest joined two empid's of Dept (1,2,3) i.e:
+-------+------------------------------------------+----------+------------+
| empid | title | Dept | JoiningDate|
+-------+------------------------------------------+----------+------------+
| 1 | Research and Development | 1 | 2015-08-06 |
| 11 | Research Assistant | 1 | 2015-08-05 |
| 2 | Consultant | 2 | 2015-08-06 |
| 12 | Consultant | 2 | 2015-08-05 |
| 3 | Medical Consultant | 3 | 2015-08-06 |
| 13 | Consultant HR | 3 | 2015-08-05 |
+-------+------------------------------------------+----------+------------+
In mysql you can use user defined variables to achieve you desired results
SELECT
t.empid,
t.title,
t.Dept,
t.JoiningDate
FROM
(
SELECT
*,
#r:= CASE WHEN #g = b.Dept THEN #r + 1 ELSE 1 END rounum,
#g:= b.Dept
FROM (
SELECT *
FROM table1
CROSS JOIN (SELECT #r:= NULL,#g:=NULL) a
WHERE Dept IN(1,2,3)
ORDER BY Dept,JoiningDate DESC
) b
) t
WHERE t.rounum <=2
DEMO
Use a correlated sub-select to count number of rows with same date but a later JoiningDate. If less than 2, return the row.
select empid, title, Dept, JoiningDate
from tablename t1
where (select count(*) from tablename t2
where t2.Dept = t1.Dept
and t2.JoiningDate > t1.JoiningDate) < 2
Query
select *
from emp_ t1
where
(
select count(*) from emp_ t2
where t2.Dept = t1.Dept
and t2.JoiningDate > t1.JoiningDate
) <= 1
and t1.Dept in (1,2,3)
order by t1.Dept;
SQL Fiddle
Can also achieve it by giving a rownumber.
Query
select t2.empid,
t2.title,
t2.Dept,
t2.JoiningDate
from
(
select empid,
title,
Dept,
JoiningDate,
(
case Dept
when #curA
then #curRow := #curRow + 1
else #curRow := 1 and #curA := Dept end
) as rn
from employee t,
(select #curRow := 0, #curA := '') r
where Dept in (1,2,3)
order by Dept,JoiningDate desc
)t2
where rn < 3;
SQL Fiddle
Related
I am stuck on how to modify my query to get the ranking results that I am looking for. I have been looking at the questions and queries on SO but can not get the results. I can get the query to do the calculation and return data but the "rank" is not correct. Any nudge would be fantastic!
Table 1 contains school data:
+-----+------------+---------------+
| SID | schoolName | schoolCountry |
+-----+------------+---------------+
| 1 | ASD | UAE |
| 2 | ASIJ | Japan |
| 3 | ASP | France |
+-----+------------+---------------+
Table 2 contains review data (my query has more columns, but this is how it works).
+-----+----------+--------+----+----+----+----+----+
| RID | schoolID | active | Q1 | Q2 | Q3 | Q4 | Q5 |
+-----+----------+--------+----+----+----+----+----+
| 1 | 1 | 1 | 8 | 9 | 5 | 1 | 9 |
| 2 | 2 | 1 | 7 | 6 | 6 | 7 | 9 |
| 3 | 1 | 0 | 1 | 4 | 7 | 8 | 5 |
| 4 | 3 | 1 | 2 | 10 | 6 | 7 | 5 |
+-----+----------+--------+----+----+----+----+----+
I am trying to create different ranks (Country, Region, Overall) by averaging the overall review score for a school. I am currently working on the country ranking and my query so far is.
SELECT SID, schoolName, rank, average
FROM (
SELECT (#rank := #rank + 1) AS rank,schools.SID, schools.schoolName,
ROUND(AVG(IF(reviews.active = 1, ((Q1+Q2+Q3+Q4+Q5+Q6+Q7+Q8+Q9+Q10+Q11+Q12+Q13+Q14+Q15+Q16+Q17+Q18+Q19+Q20+Q21+Q22+Q23+Q24+Q25+Q26+Q27+Q28+Q29+Q30+Q31+Q32+Q33+Q34+Q35+Q36+Q37+Q38+Q39+Q40+Q41+Q42+Q43+Q44+Q45+Q46+Q47+Q48+Q49+Q50+Q51+Q52)/(52*10)*10), NULL)) ,1) AS average
FROM schools
RIGHT JOIN reviews ON reviews.schoolID = schools.SID
CROSS JOIN (SELECT #rank := 0) AS vars
WHERE schools.schoolCountry = 'United Arab Emirates'
GROUP BY schools.SID
) as order_ranked
ORDER BY `order_ranked`.`average` DESC
My output comes back as:
+-----+----------------------------------------+------+---------+--+
| SID | schoolName | rank | average | |
+-----+----------------------------------------+------+---------+--+
| 568 | GEMS Wellington Primary School | 3 | 8.3 | |
| 1 | American School of Dubai | 1 | 8.1 | |
| 561 | Dubai American Academy | 4 | 7.9 | |
| 560 | Deira International School | 11 | 7.7 | |
| 569 | GEMS World Academy Dubai | 10 | 7.0 | |
| 570 | Greenfield Community School | 8 | 6.7 | |
| 565 | GEMS American Academy Abu Dhabi | 6 | 6.0 | |
| 584 | Universal American School | 5 | 5.9 | |
| 558 | American Academy In Al Mizhar | 7 | 5.5 | |
| 579 | The Cambridge High School Abu Dhabi | 9 | 4.8 | |
| 576 | Ras Al Khaimah English Speaking School | 2 | 4.3 | |
+-----+----------------------------------------+------+---------+--+
As you can see it ranks, but not correctly. I just can't figure out why.
I'm not sure why you would be using right join for this. In MySQL, you often have to sort in the subquery before using the variables.
SELECT SID, schoolName, (#rank := #rank + 1) AS rank, average
FROM (SELECT s.SID, s.schoolName,
ROUND(AVG(Q1+Q2+Q3+Q4+Q5+Q6+Q7+Q8+Q9+Q10+Q11+Q12+Q13+Q14+Q15+Q16+Q17+Q18+Q19+Q20+Q21+Q22+Q23+Q24+Q25+Q26+Q27+Q28+Q29+Q30+Q31+Q32+Q33+Q34+Q35+Q36+Q37+Q38+Q39+Q40+Q41+Q42+Q43+Q44+Q45+Q46+Q47+Q48+Q49+Q50+Q51+Q52)/(52*10)*10)) AS average
FROM schools s JOIN
reviews r
ON r.schoolID = s.SID
WHERE s.schoolCountry = 'United Arab Emirates' AND r.isactive = 1
GROUP BY schools.SID
ORDER BY average DESC
) sr CROSS JOIN
(SELECT #rank := 0) AS vars
ORDER BY average DESC
Rank should depend on the ordering of average..but there is no order by in the inner query. First compute the average and calculate rank thereafter. Also, i moved the where condition to join because the way you have it is equivalent to an inner join.
SELECT SID, schoolName, #rank := #rank + 1 AS rank, average
FROM (
SELECT schools.SID, schools.schoolName,
ROUND(AVG(IF(reviews.active = 1, ((Q1+Q2+Q3+Q4+Q5+Q6+Q7+Q8+Q9+Q10+Q11+Q12+Q13+Q14+Q15+Q16+Q17+Q18+Q19+Q20+Q21+Q22+Q23+Q24+Q25+Q26+Q27+Q28+Q29+Q30+Q31+Q32+Q33+Q34+Q35+Q36+Q37+Q38+Q39+Q40+Q41+Q42+Q43+Q44+Q45+Q46+Q47+Q48+Q49+Q50+Q51+Q52)/(52*10)*10), NULL)) ,1) AS average
FROM schools
RIGHT JOIN reviews ON reviews.schoolID = schools.SID AND schools.schoolCountry = 'United Arab Emirates'
GROUP BY schools.SID,schools.schoolName
) as order_ranked
CROSS JOIN (SELECT #rank := 0) AS vars
ORDER BY `average` DESC
I'm wondering how to select the second smallest value from a mysql table, grouped on a non-numeric column. If I have a table that looks like this:
+----+----------+------------+--------+------------+
| id | customer | order_type | amount | created_dt |
+----+----------+------------+--------+------------+
| 1 | 1 | web | 5 | 2017-01-01 |
| 2 | 1 | web | 7 | 2017-01-05 |
| 3 | 2 | web | 2 | 2017-01-07 |
| 4 | 3 | web | 2 | 2017-02-01 |
| 5 | 3 | web | 3 | 2017-02-01 |
| 6 | 2 | web | 5 | 2017-03-15 |
| 7 | 1 | in_person | 7 | 2017-02-01 |
| 8 | 3 | web | 8 | 2017-01-01 |
| 9 | 2 | web | 1 | 2017-04-01 |
+----+----------+------------+--------+------------+
I want to count the number of second orders in each month/year. I also have a customer table (which is where the customer ids come from). I can find the number of customers with more than at least 2 orders by the customer's created date by querying
select date(c.created_dt) as create_date, count(c.id)
from customer c
where c.id in
(select or.identity_id
from orders or
where
(select count(o.created_dt)
from orders o
where or.customer = o.customer and o.order_tpe in ('web')
) > 1
)
group by 1;
However, that result gives customer by their created date, and I can't seem to figure out how to find the the number of second orders by date.
The desired output i'd like to see, based on the data above, is:
+-------+------+---------------+
| month | year | second_orders |
+-------+------+---------------+
| 1 | 2017 | 1 |
| 2 | 2017 | 1 |
| 3 | 2017 | 1 |
+-------+------+---------------+
One way to approach this
SELECT YEAR(created_dt) year, MONTH(created_dt) month, COUNT(*) second_orders
FROM (
SELECT created_dt,
#rn := IF(#c = customer, #rn + 1, 1) rn,
#c := customer
FROM orders CROSS JOIN (
SELECT #c := NULL, #rn := 1
) i
WHERE order_type = 'web'
ORDER BY customer, id
) q
WHERE rn = 2
GROUP BY YEAR(created_dt), MONTH(created_dt)
ORDER BY year, month
Here is a dbfiddle demo
Output:
+------+-------+---------------+
| year | month | second_orders |
+------+-------+---------------+
| 2017 | 1 | 1 |
| 2017 | 2 | 1 |
| 2017 | 3 | 1 |
+------+-------+---------------+
I am trying to show the top 3 (or any number) housings per category. The top meaning the most visited. So if I have a table like this:
+------------+--------------------+--------+
| housing_id | category | visits |
+------------+--------------------+--------+
| 7 | cat | 2 |
| 8 | New Category | 1 |
| 10 | bead and breakfast | 1 |
| 11 | bead and breakfast | 4 |
| 15 | 2 | 3 |
| 16 | 2 | 1 |
| 17 | New Category | 1 |
| 18 | cat | 1 |
+------------+--------------------+--------+
I and I want to select the top 3 most visited housings per category so I am doing this.
select housing_id, category, visits
from
(select housing_id, category, visits,
#category_rank := if(#current_category = category, #country_rank + 1, 1) as category_rank,
#current_category := category
from visit_counts
order by category, visits desc
) ranked
where category_rank <= 3;
I get:
+------------+--------------------+--------+
| housing_id | category | visits |
+------------+--------------------+--------+
| 15 | 2 | 3 |
| 11 | bead and breakfast | 4 |
| 7 | cat | 2 |
| 8 | New Category | 1 |
+------------+--------------------+--------+
but I want:
+------------+--------------------+--------+
| housing_id | category | visits |
+------------+--------------------+--------+
| 15 | 2 | 3 |
| 16 | 2 | 1 |
| 11 | bead and breakfast | 4 |
| 10 | bead and breakfast | 1 |
| 7 | cat | 2 |
| 18 | cat | 1 |
| 8 | New Category | 1 |
| 17 | New Category | 1 |
+------------+--------------------+--------+
You are using the user variables without declaring them. Also, you should assign and read the user variables in one expression as the MySQL doesnt guarantee the order of column evaluation (so assignment may happen before or after you read it).
Try this:
select housing_id, category, visits
from (
select housing_id, category, visits,
#category_rank := if(#current_category = category,
#category_rank + 1,
if(#current_category := category, 1, 1)
) as category_rank
from visit_counts, (select #category_rank := 0, #current_category := null) t2
order by category, visits desc
) ranked
where category_rank <= 3;
Demo
Consider this data:
---+-----------+-------+--+
| id | name | grade | |
+----+-----------+-------+--+
| 13 | Maria | 10 | |
| 18 | Lorenzo | 10 | |
| 2 | Cissa | 10 | |
| 3 | Neto | 9 | |
| 15 | Gabriel | 9 | |
| 10 | Laura | 9 | |
| 12 | JoĆ£ozinho | 8 | |
| 16 | Sergio | 8 | |
| 8 | Adriele | 8 | |
| 6 | Jorgito | 8 | |
| 5 | Aline | 8 | |
| 1 | Cintia | 8 | |
| 19 | Fabiana | 7 | |
| 11 | Vinicius | 7 | |
| 9 | Tatiane | 7 | |
| 7 | Chico | 7 | |
| 4 | Marcos | 7 | |
| 14 | Pedro | 6 | |
| 17 | Mauricio | 6 | |
| 20 | Leonardo | 6 | |
+----+-----------+-------+--+
I need the students with the three biggest grades. I think I need to group the data by grade and limit to the top 3 groups.
"SELECT * FROM student GROUP BY grade LIMIT 3" only gives me 3 rows, that's not what I want.
I've tried to use HAVING to filter the groups, but without success. I don't want to set the filter grade>MAX(grade)-2, because theoretically I will not know the grades. But this filter didn't work anyway.
I'am using MySQL. Please help!
You can do this using a join:
select s.*
from student s join
(select grade
from student
group by grade
order by grade desc
limit 3
) g3
on s.grade = g3.grade;
In most databases, you an do this using in:
select s.*
from student s
where s.grade in (select grade
from student
group by grade
order by grade desc
limit 3
);
However, MySQL seems to reject this syntax.
select s1.*
from students s1
join (select distinct grade from students
order by grade desc limit 3) s2 on s1.grade = s2.grade
Alternatively:
select *
from students
where grade >= (select distinct grade from students
order by grade desc limit 2,1)
select s.*
from student s join
(select top 3 grade
from student
group by grade
order by grade desc
) g3
on s.grade = g3.grade;
Given a *students_exam_rooms* table:
+------------+---------+---------+
| student_id | room_id | seat_no |
+------------+---------+---------+
| 1 | 30 | 1001 |
| 2 | 30 | 1002 |
| 3 | 31 | 2001 |
| 4 | 32 | 2002 |
| 5 | 33 | 3001 |
| 6 | 33 | 3002 |
| 7 | 34 | 4001 |
| 8 | 34 | 4002 |
+------------+---------+---------+
And *students_tbl*:
+------------+-------------+------+
| student_id | studen_name | year |
+------------+-------------+------+
| 1 | Eric | 1 |
| 2 | Mustafa | 1 |
| 3 | Michael | 2 |
| 4 | Andy | 2 |
| 5 | Rafael | 3 |
| 6 | Mark | 3 |
| 7 | Jack | 4 |
| 8 | peter | 4 |
+------------+-------------+------+
How can I select from *students_exam_rooms* ordering by *students_tbl.year* but with one after one like this:
+--------------+------+
| student_name | year |
+--------------+------+
| Eric | 1 |
| Michael | 2 |
| Rafael | 3 |
| Jack | 4 |
| Mustafa | 1 |
| Andy | 2 |
| Mark | 3 |
| Peter | 4 |
+--------------+------+
I'm assuming that you want to order by the "occurrence-count" of the year then the year, e.g. all the first-occurrences of all years first, sorted by year, then all second-occurrences of all years also sorted by year, and so on. That would be a perfect case for emulating other RDBMS' analytic / windowing functions:
select *
from (
select
s.studen_name,
s.year,
ser.*,
(
select 1 + count(*)
from students_tbl s2
where s.year = s2.year
and s.student_id > s2.student_id
) rank
from students_tbl s
JOIN students_exam_rooms ser
ON s.student_id = ser.student_id
) i_dont_really_want_to_name_this
order by rank, year
Here it is against a slightly tweaked version of JW's fiddle: http://www.sqlfiddle.com/#!2/27c91/1
Emulating Analytic (AKA Ranking) Functions with MySQL is a good article that gives more background and explanation.
try any of these below:
SELECT a.studen_name, a.year
FROM students_tbl a
INNER JOIN students_exam_rooms b
ON a.student_id = b.student_id
ORDER BY REVERSE(b.seat_no),
a.year
SQLFiddle Demo
by using Modulo
SELECT a.studen_name, a.year
FROM students_tbl a
INNER JOIN students_exam_rooms b
ON a.student_id = b.student_id
ORDER BY CASE WHEN MOD(b.seat_no, 2) <> 0 THEN 0 ELSE 1 END,
a.year
SQLFiddle Demo
Looks to me like you're trying to sort first by seat and then by year. Looking at your students_exam_rooms table, it looks like you started with a simple seat number and prepended year * 1000. So, if we omit the year, it looks like this:
> select * from fixed_students_exam_rooms;
+------------+---------+---------+
| student_id | room_id | seat_no |
+------------+---------+---------+
| 1 | 30 | 1 |
| 2 | 30 | 2 |
| 3 | 31 | 1 |
| 4 | 32 | 2 |
| 5 | 33 | 1 |
| 6 | 33 | 2 |
| 7 | 34 | 1 |
| 8 | 34 | 2 |
+------------+---------+---------+
And if you had that table, your query is simple:
select
student_name, year
from
modified_student_exame_rooms
left join students_tbl using (student_id)
order by
seat_no, year
;
Using the table as you currently have it, it's only slightly more complicated, assuming the "core seat number" doesn't excede 999.
select
student_name, year
from
modified_student_exame_rooms
left join students_tbl using (student_id)
order by
convert(substr(seat_no, 2), unsigned),
year
;