select max salary in sql - mysql

Emp table
id ename
----------- ----------
1 apv
2 sug
3 raj
4 ram
5 sam
6 apv1
7 sug1
8 raj1
9 ram1
10 sam1
Dept Table
dept salary id
----- ----------- -----------
a 1000 1
b 2000 2
c 3000 3
d 5000 4
e 7000 5
a 20000 6
b 500 7
c 5000 8
a 1000 9
b 90000 10
How can I return both the Max(salary) from each dept and also the details of who earns that salary?
(select id,ename,dept,salary)

select mm, d1.dept, d1.id, ename from DEPT D1,
(select max(salary) mm, dept from DEPT group by dept) D2, EMP
where D2.mm=d1.salary and d2.dep=d1.dept
EMP.ID=DEPT.ID

declare #Emp table (id int, ename varchar(4))
declare #Dept table (dept char(1), salary int, id int)
insert into #Emp values
(1, 'apv'),
(2, 'sug'),
(3, 'raj'),
(4, 'ram'),
(5, 'sam'),
(6, 'apv1'),
(7, 'sug1'),
(8, 'raj1'),
(9, 'ram1'),
(10, 'sam1')
insert into #Dept values
('a', 1000, 1),
('b', 2000, 2),
('c', 3000, 3),
('d', 5000, 4),
('e', 7000, 5),
('a', 20000, 6),
('b', 500, 7),
('c', 5000, 8),
('a', 1000, 9),
('b', 90000, 10)
;with cte as
(
select
id,
salary,
dept,
rank() over(partition by dept order by salary desc) as rn
from #Dept
)
select
e.ename,
e.id,
c.salary,
c.dept
from cte as c
inner join #Emp as e
on c.id = e.id
where rn = 1
Result
ename id salary dept
----- ----------- ----------- ----
apv1 6 20000 a
sam1 10 90000 b
raj1 8 5000 c
ram 4 5000 d
sam 5 7000 e

Prequery by department first, then go back to itself on same department and matching salary. From THAT table, you can tie to the employee table. This version will allow multiple people in the same department with the same salary to be pulled out... ex: 5 people in Dept X earning 75,000.
SELECT
d1.Dept,
d1.Salary,
Emp.id,
Emp.name
from
( SELECT
dept,
MAX(salary) MaxSal
from
Dept
group by
dept ) ByDept
join Dept d1
ON ByDept.dept = d1.dept
and ByDept.MaxSal = d1.Salary
join Emp
ON d1.id = Emp.id

I do not know if I understand you at all but you can try something like:
SELECT id, ename, dept, MAX(salary) AS salary
FROM Dept_Table AS d
LEFT JOIN Emp_Table AS e
ON e.id = d.id
GROUP BY dept

select dept.dept, max(dept.salary), emp.id, emp.ename
from emp inner join dept on emp.id=dept.id
group by dept.dept, emp.id, emp.ename
should do the trick, just know that if two people in same dept have same salary and it is the maximum salary you will have both people as outout.

SELECT emp.ename,dept.dept,max(dept.salary) from dept left join emp on dept.id=emp.id group by dept.dept

Related

List customers who haven't ordered any products that another one has

These three tables are part of a larger order management system:
orders
o_id c_id
1 1
2 1
3 2
4 3
5 3
6 4
7 5
order_items
o_id p_id
1 1
2 2
3 1
3 2
3 8
4 1
4 2
5 8
5 9
6 4
6 5
7 12
customers
c_id name
1 Doug
2 Tammy
3 Bill
4 Don
5 Kate
I want to find ALL pairs of customers where the second customer in the pair has purchased NONE of the products that the first customer in the pair has purchased. I can't seem to figure this out! My best attempt was grabbing the count of all unique products and trying to see if I could group and reduce by leveraging that count.
Expected Output
c_id1 c_id2
4 1
4 2
4 3
4 5
5 1
5 2
5 3
Or the exact opposite (no duplicates).
CREATE TABLE orders (
o_id INT,
c_id INT
);
INSERT INTO orders (o_id, c_id) VALUES
(1, 1),
(2, 1),
(3, 2),
(4, 3),
(5, 3),
(6, 4),
(7, 5);
CREATE TABLE order_items (
o_id INT,
p_id INT
);
INSERT INTO order_items (o_id, p_id) VALUES
(1, 1),
(2, 2),
(3, 1),
(3, 2),
(3, 8),
(4, 1),
(4, 2),
(5, 8),
(5, 9),
(6, 4),
(6, 5),
(7, 12);
CREATE TABLE customers (
c_id INT,
name VARCHAR(10)
);
INSERT INTO customers (c_id, name) VALUES
(1, 'Doug'),
(2, 'Tammy'),
(3, 'Bill'),
(4, 'Don'),
(5, 'Kate');
Test
WITH cte AS ( SELECT *
FROM orders
NATURAL JOIN order_items
NATURAL JOIN customers )
SELECT t1.c_id id1, t2.c_id id2
FROM customers t1
JOIN customers t2 ON t1.c_id < t2.c_id
WHERE NOT EXISTS ( SELECT NULL
FROM cte cte1, cte cte2
WHERE cte1.c_id = t1.c_id
AND cte2.c_id = t2.c_id
AND cte1.p_id = cte2.p_id );
fiddle
The idea is to generate all pairs of customers (using a cross join).
Then check that they do not have the same items. This is a little tricky, but it involves not exists and joining down to the item level to see if any match on orders that match the customers:
select c1.c_id, c2.c_id
from customers c1 cross join
customers c2
where not exists (select 1
from order_items oi1 join
order_items oi2
on oi1.i_id = oi2.i_id join
orders o1
on o1.o_id = oi1.o_id join
orders o2
on o2.o_id = oi2.o_id
where o1.c_id = c1.c_id and
o2.c_id = o2.c_id
);

Create a Rank Column based on the max values of a distinct column that belongs to a group set

I need to create a ranking column with a rank for each distinct part_id of that is associated to an order_id based on the MAX value of build_steps for that part_id. The rank should be restarted whenever a new order_id is iterated.
I have the following fiddle but it is not creating the rank properly.
http://sqlfiddle.com/#!9/63d47/29
Below is my query
SET #current_rank = 0;
SET #prevOrder = null;
SELECT part_id, MAX(build_steps) AS max_build_steps,
#current_rank:= CASE WHEN #prevOrder = order_id
THEN #current_rank:=#current_rank +1
ELSE #current_rank:=1 END rank,
#prevOrder:= order_id as 'order_id'
FROM orders,
(SELECT #current_rank:=0) r
GROUP BY order_id, part_id
ORDER BY order_id desc, max_build_steps desc;
Table:
CREATE TABLE orders
(`part_id` int, `build_steps` int, `order_id` int)
;
INSERT INTO orders
(`part_id`, `build_steps`, `order_id`)
VALUES
(234554, 1, 1234),
(234554, 2, 1234),
(234554, 3, 1234),
(234554, 4, 1234),
(234554, 5, 1234),
(234554, 6, 1234),
(234554, 7, 1234),
(234554, 8, 1234),
(234555, 1, 1234),
(234555, 2, 1234),
(234556, 1, 1234),
(234556, 2, 1234),
(234556, 3, 1234),
(234557, 1, 1234),
(234566, 1, 5678),
(234566, 2, 5678),
(234566, 3, 5678),
(234566, 4, 5678),
(234566, 5, 5678),
(234567, 1, 5678),
(234567, 2, 5678),
(234568, 1, 5678),
(234569, 1, 5678)
;
Expected Result:
part_id, max_build_steps, rank, order_id
234566 5 1 5678
234567 2 2 5678
234568 1 3 5678
234569 1 4 5678
234554 8 1 1234
234556 3 2 1234
234555 2 3 1234
234557 1 4 1234
Current Query Results:
part_id max_build_steps rank order_id
234566 5 1 5678
234567 2 2 5678
234568 1 3 5678
234569 1 4 5678
234554 8 1 1234
234556 3 3 1234
234555 2 2 1234
234557 1 4 1234
Your question is a good demonstration that using session variables for such tasks relying on the engines execution order is ustable. In your case it seems that MySQL executes your operations in the SELECT part before it orders the result set. A workaround is to use an ordered subquery:
select part_id,
max_build_steps,
case when order_id = #order_id
then #rank := #rank + 1
else #rank := 1
end as rank,
#order_id := order_id as order_id
from (
select o.part_id, o.order_id, max(build_steps) as max_build_steps
from orders o
group by o.part_id, o.order_id
order by o.order_id, max_build_steps desc
limit 1000000000
) sub
cross join (select #order_id := null, #rank := 0) init_session_vars
That works at least on sqlfiddle with MySQL 5.6. However people already reported issues, that newer MySQL versions do not order subquery results. That's why i added a huge limit, which is a workaround for MariaDB (cann't say if it works for MySQL 5.7).
However - you better fetch the ordered result and calculate the rankings in a procedural language where the execution order is guaranteed.
Update
Here is a different approach using temporary tables and an AUTO_INCREMENT column:
create temporary table tmp1 (
ai int auto_increment primary key,
part_id int,
order_id int,
max_build_steps int
) select o.part_id, o.order_id, max(build_steps) as max_build_steps
from orders o
group by o.part_id, o.order_id
order by o.order_id, max_build_steps desc
;
create temporary table tmp2 (order_id int, min_ai int)
select order_id, min(ai) as min_ai
from tmp1
group by order_id
;
select t1.part_id,
t1.order_id,
t1.max_build_steps,
t1.ai - t2.min_ai + 1 as rank
from tmp1 t1
join tmp2 t2 using(order_id);
sqlfiddle

Distribute records in SQL based on count

I have a requirement to distribute records equally into two categories. But in case I fall short of records in any one category, I should accommodate count the remaining records in other category.
Sample data:
If like this students of subject s1 are 12, and subject s2 are 20. I need to pick 30 students, result should give me 15 for each subject, but as s1 total is only 12, I should get 12 from s1 and 18 from s2.
This should do the trick:
DECLARE #t TABLE(ID INT, Student VARCHAR(10), Subject CHAR(2))
INSERT INTO #t VALUES
(1, 'Stud1', 's1'),
(2, 'Stud2', 's1'),
(3, 'Stud3', 's2'),
(4, 'Stud4', 's2'),
(5, 'Stud5', 's2'),
(6, 'Stud6', 's2'),
(7, 'Stud7', 's2'),
(8, 'Stud8', 's2'),
(9, 'Stud9', 's2')
;WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY Subject ORDER BY ID) AS rn FROM #t)
SELECT TOP 7 *
FROM cte
ORDER BY rn, Subject
The idea is that you are numbering the rows within subjects like:
1 Stud1 s1 1
3 Stud3 s2 1
2 Stud2 s1 2
4 Stud4 s2 2
5 Stud5 s2 3
6 Stud6 s2 4
7 Stud7 s2 5
8 Stud8 s2 6
9 Stud9 s2 7
So, when selecting top N rows, they are distributing automatically because of ordering by that column.

Query to count the top three items that appear multiple times

In the query below I have a basic table with two fields course_name and course_id. I am trying to display through the query the top three courses by the amount of times they appear in the table. I am using COUNT, then I am comparing the values in additional select to determine the top three and finally display the results by GROUP by course_id. I am getting a mysql syntax problem. How can I display the top three courses with the amount of times they appear in the table? FIDDLE
SELECT course_name, course_id, COUNT(1) AS cnt
FROM courses
JOIN (SELECT distinct cnt cnt3
FROM courses
ORDER BY cnt DESC
LIMIT 2, 1) x
ON cnt >= cnt3
ORDER by cnt DESC
GROUP BY course_id
Table Schema:
CREATE TABLE courses
(`course_name` varchar(15), `course_id` int)
;
INSERT INTO courses
(`course_name`, `course_id`)
VALUES
('Math', 1),
('Science', 2),
('PHYS', 3),
('Study Hall', 4),
('History', 5),
('Social Studies', 6),
('Math', 1),
('PHYS', 3),
('Math', 1),
('Science', 2),
('Science', 2),
('Study Hall', 4),
('History', 5)
;
Desired Result:
+-------------+-------+
| Course_name | Count |
+-------------+-------+
| Math | 3 |
| Science | 3 |
| PHYS | 2 |
| Study | 2 |
| History | 2 |
+-------------+-------+
Your syntax error is due to this:
ON cnt >= cnt3
cnt is an alias and you can't use it in a join. Also, your order by and group by clauses are in the wrong order.
Edit starts here
Looking at your query, you may have overengineered it. Would this not give you your answer?
select course_name, course_id, count(*) records
from courses
group by course_name, course_id
having count(*) > 1
order by records desc
limit 3
Select course_name,cnt from(
Select course_id,course_name,count(course_id) as cnt group by course_id,course_name
)tmp
Order by cnt desc limit 0,3

self join plus another join

I have a table of country teams
id country group
1 Poland 1
2 Greece 1
3 England 2
4 France 2
5 Germany 3
6 Spain 3
I also a table of scores for each country
fromId score
1 100
3 50
2 90
4 60
What I would like to do is get back a table of scores for each country within a group, having supplied just a country name. For example if I supply "France" then I would want the following table back
country score
England 50
France 60
I have managed to self join the country table with
SELECT
`t1`.`fldCountry`,
`t1`.`fldID`
FROM tblteam t1, tblteam t2
WHERE
t2.fldTeam = t1.fldTeam
AND
t2.fldCountry = 'France'
but am now stuck how to joing this back to get the data!
Please could anyone help a little?
Here you go
Country Table:
CREATE TEMPORARY TABLE Country
(
id INT,
country VARCHAR(20),
grp INT
)
INSERT INTO Country
(
id,
country,
grp
)
SELECT 1,
'Poland',
1
UNION ALL
SELECT 2,
'Greece',
1
UNION ALL
SELECT 3,
'England',
2
UNION ALL
SELECT 4,
'France',
2
UNION ALL
SELECT 5,
'Germany',
3
UNION ALL
SELECT 6,
'Spain',
3
Score Table:
CREATE TEMPORARY TABLE Score ( fromid INT, score INT )
INSERT INTO Score
(
fromid,
score
)
SELECT 1,
100
UNION ALL
SELECT 3,
50
UNION ALL
SELECT 2,
90
UNION ALL
SELECT 4,
60
Query:
SELECT b.country,
IFNULL(s.score, 0) score
FROM Country a
INNER JOIN Country b
ON a.grp = b.grp
LEFT JOIN score s
ON s.fromid = b.id
WHERE a.country = 'France'
Result:
Country Score
England 50
France 60
SELECT
c.country,
s.score
FROM
countries c
INNER JOIN scores s ON c.id = s.fromId
WHERE
c.group = (SELECT group FROM countries WHERE country = 'France' LIMIT 1);
Something like this should work.
Select C2.Country,SUM(S.Score)
FROM Country AS C1
INNER JOIN Country AS C2
ON C1.Group=C2.Group
INNER JOIN Scores AS S
ON C2.id = S.FromID
WHERE C1.Country=#Country
GROUP BY C2.Country
Where #Country is the country you want to look up, in your case, France.