I want to find the largest sale for each of my employees (and display the name of the employee). In MySQL, it's pretty straightforward:
select *
from employee, sale
where employee.id = sale.employee_id
group by employee_id
order by sale.total desc
This does pretty much what one would expect, it would return a list of employees and end up returning the largest sale record with the employee row.
But, Oracle does not allow you to return columns which are not group by expressions when a group by clause is used. Do this make what I do in MySQL "impossible" in Oracle? Or is there some workaround? I suppose I could perform some sort of subquery, but not sure if there is another way to do this that wouldn't quite be so complicated to construct.
Get rid of your select * and replace it with just the columns you need and then group by all the "non-processed" columns.
You'll end up with something like:
select employee.id, employee.name, max(sale.total)
from employee, sale
where employee.id = sale.employee_id
group by employee.id, employee.name
order by max(sale.total) desc
It's a pain - I've had to do this many times before - but just add all the related columns to your group by
To get the largest sale you can use group by with the max function.
select e.name, max (s.total)
from employee e, sale s
where e.id = s.employee_id
group by e.name
order by s.total desc
I have made an assumption that the employee name is in the name column of the employee table. I have also aliased the employee table and sales tables.
If you would prefer to see the total sales for an employee, you can swap out max() and use sum() instead.
Congratulations, you've learned just enough to be dangerous!
What you really want is each employee's largest sale. Now it happens that sorting them by sales amount desc and then grouping them works in MySQL, even though that isn't legal according to ANSI SQL. (Basically, MySQL is arbitrarily grabbing the first row for each employee, and that "works" because of the sort.)
The right way to do this is not to rely on the side of effect of the sort doing what you want; instead you should explicitly ask for what you want: the largest sale for each employee. In SQL that's:
select employee.id, max( sale.total)
from employee, sale
where employee.id = sale.employee_id
group by employee.id
order by 2
If you want to select an employee with a highest sale, you don't need GROUP BY here at all.
All you need is to select the highest sale and join it back to the employees:
SELECT *
FROM (
SELECT sale.*, ROW_NUMBER() OVER (ORDER BY total DESC) AS rn
FROM sale
) s
JOIN employee e
ON e.id = s.employee_id
AND s.rn = 1
This will select a single row with a total highest sale.
If you want to select per-employee highest sale, just add a PARTITION BY clause to your query:
SELECT *
FROM (
SELECT sale.*, ROW_NUMBER() OVER (PARTITION BY employee_id ORDER BY total DESC) AS rn
FROM sale
) s
JOIN employee e
ON e.id = s.employee_id
AND s.rn = 1
Related
Table employee has two columns:
ID
NAME
Table external_job also has two columns:
ID
SALARY
I have to get one person who got the maximum salary.
The result must have three columns and one row:
ID
NAME
SALARY
I made a query but the client asked me not to use a sub-select query.
How can I do in this case?
My query is:
select *
from (select a.id,
a.name,
(select sum(salary)
from external_job b
where b.id = a.id) salary
from employee a
order by salary desc)
where rownum = 1
Use order by and some method for limiting the results to one row. In standard SQL this is:
select ej.id, e.name, ej.salary
from employee e join
external_job ej
on ej.id = e.id
order by ej.salary
fetch first 1 row only;
Not all databases support fetch first. Some use limit or select top or even more arcane constructs.
Assumption is ID is primary key on both sides.
select ID, NAME, SALARY from employee e , external_job a where e.id= a.id
order by salary desc
limit 1
I have a table that is called: customers.
I'm trying to get the name and the salary of the people who have the maximum salary.
So I have tried this:
SELECT name, salary AS MaxSalary
FROM CUSTOMERS
GROUP BY salary
HAVING salary = max(salary)
Unfortunately, I got this error:
Column 'CUSTOMERS.name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I know I should add the name column to the group by clause, but I get all the records of the table.
I know that I can do it by:
SELECT name, salary
FROM CUSTOMERS
WHERE salary = (SELECT MAX(salary) FROM CUSTOMERS)
But I want to achieve it by group by and having clauses.
This requirement isn't really suited for a group by and having solution. The easiest way to do so, assuming you're using a modern-insh version of MS SQL Server, is to use the rank window function:
SELECT name, salary
FROM (SELECT name, salary, RANK() OVER (ORDER BY salary DESC) rk
FROM customers) c
WHERE rk = 1
Mureinik's answer is good with rank, but if you didn't want a windowed function for whatever reason, you can just use a CTE or a subquery.
with mxs as (
select
max(salary) max_salary
from
customers
)
select
name
,salary
from
customers cst
join mxs on mxs.max_salary = cst.salary
There was no need to use group by and having clause there, you know. But if you want to use them then query should be
SELECT name, salary
FROM CUSTOMERS
GROUP BY salary
having salary = (select max(salary) from CUSTOMERS)
In my Employee table, I wanted to find the 3rd highest salary. Someone provided me with the following query to do this:
SELECT *
FROM employee C1
WHERE 3 = (SELECT Count(DISTINCT( C2.salary ))
FROM employee C2
WHERE C2.salary >= C1.salary)
This query works, but I don't how it works. What kind of query is this?
As others have said, this type of query is called a correlated sub-query. It's a sub-query because there is a query within a query and it's correlated because the inner query references the outer query in its definition.
Consider the inner query:
SELECT Count(DISTINCT( C2.salary ))
FROM employee C2
WHERE C2.salary >= C1.salary
Conceptually, this inner query will be evaluated once for every row produced by the outer query before the WHERE clause is applied, basically once for every row in employee. It will produce a single value, the count of rows from employee where the salary is less than the salary of the outer row.
The outer query will only return records where the value produced by the inner query is exactly 3. Assuming unique salary values, there is only one row from the employee table where there will be exactly 3 records with a salary value greater than or equal to it (the one row) and that one row is necessarily the third-highest salary value.
It's clever, but unnecessarily weird and probably not as optimal as something more straightforward.
Maybe a better solution would have been
SELECT TOP 1 *
FROM (
SELECT TOP 3 * FROM employee ORDER BY Salary DESC
) t
ORDER BY Salary ASC
Easier to read and more efficient than a correlated sub-query.
You can also use Dense_Rank to rank the salaries greatest to least, then select the ones that are ranked 3rd. This will also prevent you from getting the wrong salary if the top 2 are identical like the other answers above mine are doing. This has a better looking execution plan than the Distinct count one also
SELECT *
FROM (
SELECT *,
DENSE_RANK() OVER (ORDER BY Salary DESC) salary_rank
FROM employee e
) t
WHERE salary_rank = 3
Could also rewrite this with a common table expression.
WITH top_three
AS
(
SELECT TOP 3 * FROM employee ORDER BY Salary DESC
)
SELECT TOP 1 *
FROM top_three
ORDER BY Salary ASC;
Or, if you need to look for other ranks in this you can use row_number().
WITH ranked
AS
(
SELECT rank = ROW_NUMBER()OVER(ORDER BY Salary DESC), *
FROM employee
ORDER BY Salary DESC
)
SELECT *
FROM ranked
WHERE rank = #whatever_rank_you_want;
I am trying to Select the name, email address that has the highest individual sale for the current month but for some reason my select statement won't return the right date or the highest amount. Any guidance or help on right direction would be appreciated
Select
FirstName, LastName,Email, SaleDate, Max(Total)as HighestTotal
From
Sale, Employee
where
month(SaleDate) = 12
Group by
SaleDate, FirstName, LastName, Email
Use TOP 1 with ORDER BY HighestTotal desc to get the highest individual sale for the given month
Also use proper INNER JOIN syntax to join two tables. Considering that both the tables have emp_id as common column
SELECT TOP 1 WITH ties E.FirstName,
E.LastName,
E.Email,
S.SaleDate,
S.Total AS HighestTotal
FROM Sale S
INNER JOIN Employee E
ON E.emp_id = S.Emp_id
WHERE Month(S.SaleDate) = 12
ORDER BY HighestTotal DESC
With Ties will bring more than one employee in case of tie in highest individual sale.
1) You have to add a condition for the current year:
where month( SaleDate) = 12 AND year(SaleDate) = 2015
2) You have to add ORDER BY to get the highest value:
ORDER BY HighestTotal DESC
LIMIT 1
I have a employee and employee_history table. For every update on employee table, I insert a record into the employee_history table.
Both the tables have a column called as effective date which indicates that the employee status as on that effective date.
Below are my two tables.
I need to get the employee record latest as on that effective date.
e.g If I need to get the employee as on 16 may I should get the emp_hist_id = 2 record from history table. As on 5 june I should get the emp_hist_id = 4 from hist table.
And as on 15th August I should get the record from employee table itself.
Please help.
You could try something like
SELECT * FROM (
SELECT * FROM (
(SELECT emp_id, name, email, title, region, division, "effective date"
FROM employee
WHERE emp_id = desired_id)
UNION
(SELECT emp_id, name, email, title, region, division, "effective date"
FROM employee _history
WHERE emp_id = desired_id)
) t
WHERE t."effective date" <= desired_date
ORDER by t."effective date" DESC) p
WHERE ROWNUM = 1
The idea is to take records related to desired user from both tables, then take dates lower than desired one and finally catch the first
You can solve this problem without analytic functions. The subquery here calculates the most recent effective date in the history record before the employee effective date:
select e.*
from employee_history eh join
(select e.employee_id, max(eh.effective_date) as latest_effective_date
from employee e join
employee_history eh
on e.employee_id = eh.employee_id and
e.effective_date >= eh.effective_date
) ehl
on eh.employee_id = ehl.employee_id and
eh.effective_date = ehl.effective_date
This solution assumes that there are no duplicate effective dates in the history table. If there are, then you have another option. Assuming the employee_history_ids are assigned sequentially, take the max of that id instead of the date.
There are alternative formulations of the solution using Oracle's analytic functions.
Try the following :
SELECT *
FROM
(SELECT e.emp_id,
e.name,
e.email,
e.title,
e.region,
e.division,
e.effective_date,
row_number() over (partition BY e.emp_id
ORDER BY e.effective_date DESC) rn
FROM employee e
UNION ALL
SELECT eh.emp_id,
eh.name,
eh.email,
eh.title,
eh.region,
eh.division,
eh.effective_date,
row_number() over (partition BY eh.emp_id
ORDER BY eh.effective_date DESC) rn
FROM employee_history eh)
WHERE effective_date < to_date('15/06/2012','DD/MM/YYYY')
AND rn = 1
you can see a better example here