how to find less than the largest number in sql? - sql

i want to show the salaries less than the largest number in the table
create table instructor(
ID number not null ,
name varchar(20) ,
dept_name varchar(25),
salary number,
primary key (ID)
);
insert into instructor values('10101', 'sirinvasan' , 'comp csi' , '65000');
insert into instructor values('12121', 'wu' , 'finance' , '90000');
insert into instructor values('15151', 'mozart' , 'music' , '40000');
insert into instructor values('22222', 'einstein' , 'physics' , '95000');
insert into instructor values('32343', 'el said' , 'history' , '60000');
i tried using
select distinct T.salary
from instructor as T , instructor as S
where T.salary < S.salary
but it gives me this error ORA-00933: SQL command not properly ended

You can do it in a single table scan using analytic functions:
SELECT salary
FROM (
SELECT DISTINCT
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM instructor
)
WHERE rnk > 1
Or, if you want to use a sub-query:
SELECT DISTINCT
salary
FROM instructor
WHERE salary NOT IN (SELECT MAX(salary) FROM instructor);
db<>fiddle here

Try this
Select salary from table where salary < (Select
max(salary) from table)

There are many ways to do this. Here is another one.
with a as (
select salary
, max(salary) over () as maxsalary
from instructor
)
select distinct salary
from a
where salary < maxsalary

In the name of completeness, here is another viable answer:
SELECT DISTINCT i.salary
FROM instructor i
ORDER BY i.salary DESC
OFFSET 1 ROW;
I actually like the simplicity of this one. It is just ordering the query and skipping the first row. No analytic functions necessary.
DBFiddle Here: (LINK)

Related

Transferring SQL query from with clause to without with clause

Consider the query:
Find all departments where the total salary is greater than the average of the total salary at all departments
with dept_total (dept_name, value) as
(
select dept_name, sum(salary)
from instructor
group by dept_name
),
dept_total_avg(value) as
(
select avg(value)
from dept_total
)
select dept_name
from dept_total, dept_total_avg
where dept_total.value >= dept_total avg.value;
Rewrite this query without using the with construct.
The query is based on University schema which is provided by The database system concept by Korth. I assume I need to consider only the instructor table to find the answer of the query.
Instructor (ID, name, dept_name, salary)
I can found the average of total salary of all dept
SELECT AVG(salary) GROUP BY dept_name;
Then I lost. I did not find the way to proceed.
I found that. But I am looking for more explanation as I cannot understand it from this link.
Thank you for help.
Here's a few ways to do it in a mySQL version that doesn't support CTEs (I'm assuming that's why they are having you rewrite the query to omit the with). Also, you are calculating the average salary by department, but the question is asking to find the average TOTAL salary of each department, then return the ones that fall above that.
https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=fefea829cff3e20aea93634f9a4114d6
With a subquery:
SELECT dept_name
FROM (
SELECT dept_name, sum(salary) as salaries
FROM instructor GROUP BY dept_name
) d
WHERE salaries > (
SELECT avg(salaries) as avgSalaries FROM (
SELECT dept_name, sum(salary) as salaries
FROM instructor GROUP BY dept_name
) z
)
With a join:
SELECT dept_name
FROM (
SELECT dept_name, sum(salary) as salaries
FROM instructor GROUP BY dept_name
) d
CROSS JOIN (
SELECT avg(salaries) as avgSalaries FROM (
SELECT dept_name, sum(salary) as salaries
FROM instructor GROUP BY dept_name
) z
) avgs
WHERE salaries > avgs.avgSalaries
With a session variable (this is deprecated in later versions, but does still work):
SELECT avg(salaries) INTO #avg FROM (
SELECT dept_name, sum(salary) as salaries
FROM instructor GROUP BY dept_name
) z;
SELECT #avg;
SELECT dept_name
FROM instructor GROUP BY dept_name
HAVING sum(salary) > #avg;
All good techniques to understand.
Read your last question and found that you are using mysql5.8, then you can use the analysis function
select distinct t1.dept_name
from (
select t1.*,
sum(salary) over(partition by dept_name) val,
sum(salary) over() / count(distinct dept_name) over() avg
from Instructor t1
) t1
where t1.val > t1.avg

Oracle SQL - Get name from table based on group by

I have a table called employee and has columns as follows
emp_id number
emp_name varchar(30)
salary float
dept_id number
I want to get the output as any one name of employee within that department and employee count from each department. I tried the below, but didn't work well
SELECT emp_name, count(*) FROM emp
GROUP BY dept_id, emp_name;
Expected output:
emp_name, count(*)
abc, 4
def, 2
xyz, 10
Can anyone suggest?
You can try this if you want just a basic "random employee" shown for each department.
select emp_name, emp_count
from (
select emp_name, dept_id,
count(*) over (partition by dept_id) emp_count,
row_number() over (partition by dept_id
order by dbms_random.value ) rnum
from employee
)
where rnum = 1
/
This uses analytic function to calculate the counts, and then pick off 1 random row to display.

Using the MIN function in the having clause

I want to get the name of the employee who has the minimum salary. Is there a way to do this using only one query? I have given my query below, it doesn't work because the having clause requires a condition. Is there any way to give a condition in the having clause that will retreive the employee name with the minimum salary?
SELECT first_name,min(salary) as "sal"
FROM Employees
GROUP BY first_name
having min(salary);
How about using ROWNUM?
SELECT *
FROM(SELECT first_name, salary
FROM Employees
ORDER BY salary
) WHERE ROWNUM = 1
SELECT first_name, salary as "sal"
FROM employees
WHERE salary =(SELECT MIN(salary)
FROM employees);
Try this solution, inspired from here:
SELECT e1.first_name, e1.salary AS "sal"
FROM Employees e1
LEFT OUTER JOIN Employees e2
ON (e1.id <> e2.id AND e1.salary > e2.salary)
WHERE e2.id IS NULL;
With a single SELECT statement:
SELECT MIN( first_name ) KEEP ( DENSE_RANK FIRST ORDER BY salary ASC, first_name ASC ) AS first_name,
MIN( salary ) KEEP ( DENSE_RANK FIRST ORDER BY salary ASC, first_name ASC ) AS salary
FROM Employees;
SQLFIDDLE
However, if there are multiple people with the same minimum salary then this will only get the one with the name which is first alphabetically.
You can get all the names, but it does require multiple SELECT statements:
SELECT first_name, salary
FROM Employees
WHERE salary = ( SELECT MIN(salary) FROM Employees );
But having multiple SELECT statements isn't a bad thing.
SQLFIDDLE
SELECT TOP 1 WITH TIES *
FROM employees
ORDER BY salary ASC
If you need the employee with the lowest salary why don't you use Order By ..
SELECT top 1 first_name,min(salary) as LowestSalary
FROM Employees order by Salary asc
SELECT first_name,min(salary) as "sal"
FROM Employees
GROUP BY first_name
having min(salary) >0;
If you want to do this with only one query, while also retrieving all employees with the minimum salary (example: you have a minimum salary of $40,000, but two employees have this exact salary) you could join the table with itself. This solution also uses the 'having' clause that you included in your original question.
SELECT e.first_name,e.salary AS "sal"
FROM Employees e, Employees e2
GROUP BY first_name
HAVING MIN(e2.salary)=e.salary;

SQL command for finding the second highest salary

HI,
Can u tell me the syntax of the SQL command which gives as output the second highest salary from a range of salaries stored in the employee table.
A description of the SQL commnd will be welcomed...
Please help!!!
select min(salary) from
(select top 2 salary from SalariesTable order by salary desc)
as ax
This should work:
select * from (
select t.*, dense_rank() over (order by salary desc) rnk from employee t
) a
where rnk = 2;
This returns the second highest salary.
dense_rank() over is a window function, and it gives you the rank of a specific row within the specified set. It is standard SQL, as defined in SQL:2003.
Window functions are awesome in general, they simplyfy lots of difficult queries.
Slightly different solution:
This is identical except that returns the highest salary when there is a tie for number 1:
select * from (
select t.*, row_number() over (order by salary desc) rnk from employee t
) a
where rnk = 2;
Updated: Changed rank to dense_rank and added second solution. Thanks, IanC!
with tempTable as(
select top 2 max(salary) as MaxSalary from employee order by salary desc
) select top 1 MaxSalary from tempTable
description:
select the top 2 maximum salaries
order them by desc order ( so the 2nd highest salary is now at the top)
select the top 1 from that
another approach:
select top 1 MaxSalary from (
select top 2 max(salary) as MaxSalary from employee order by salary desc
)
Here's some sample code, with proof of concept:
declare #t table (
Salary int
)
insert into #t values (100)
insert into #t values (900)
insert into #t values (900)
insert into #t values (400)
insert into #t values (300)
insert into #t values (200)
;WITH tbl AS (
select t.Salary, DENSE_RANK() OVER (order by t.Salary DESC) AS Rnk
from #t AS t
)
SELECT *
FROM tbl
WHERE Rnk = 2
DENSE_RANK is mandatory (change to RANK & you'll see).
You'll also see why any SELECT TOP 2 queries won't work (without a DISTINCT anyway).
You don't specify the actual SQL product you're using, and the query language varies among products. However, something like this should get you started:
SELECT salary FROM employees E1
WHERE 1 = (SELECT COUNT(*) FROM employee E2 WHERE E2.salary > E1.salary)
(thanks to fredt for the correction).
Alternatively (and faster in terms of performance) would be
SELECT TOP 2 salary FROM employees ORDER BY salary DESC
and then skipping the first returned row.
An alternative (tested):
select Min(Salary) from (
select distinct TOP (2) salary from employees order by salary DESC) AS T
This will work on any platform, is clean, and caters for the possibility of multiple tied #1 salaries.
Select top 1 * from employee where empID in (select top 2 (empID) from employee order by salary DESC) ORDER BY salary ASC
Explanation:
select top 2 (empID) from employee order by salary DESC would give the two records for which Salary is top and then the whole query would sort it these two records in ASCENDING order and then list out the one with the lowest salary among the two.
EX. let the salaries of employees be 100, 99, 98,,50.
Query 1 would return the emp ID of the persons with sal 100 and 99
Whole query would return all data related to the person having salary 99.
SELECT Salary,EmpName
FROM
(
SELECT Salary,EmpName,ROW_NUMBER() OVER(ORDER BY Salary) As Rank
FROM EMPLOYEE
) A
WHERE A.Rank=n;
where n is the number of highest salary u r requesting the table.
Ucan also use DenseRank() function in place of ROW_NUMBER().
Thanks,
Suresh
An easier way..
select MAX(salary) as SecondMax from test where salary !=(select MAX(salary) from test)

what is the query to return Name and Salary of employee Having Max Salary

what is the query to return Name and Salary of employee Having Max Salary
SELECT Name, Salary FROM Minions
WHERE Salary = (SELECT Max(Salary) FROM Minions)
Note that this will return more than one row if there is more than one employee who have the same max salary
select name, salary from (select * from salary_table order by salary desc limit 1)
SELECT FirstName, max(salary)
FROM Employees
WHERE salary = (
SELECT max(salary)
FROM employees
)
GROUP BY FirstName
working in SQL SERVER 2012
A couple of proprietary solutions
SELECT TOP 1 [WITH ties] Name, Salary
FROM employee
ORDER BY Salary DESC
SELECT Name, Salary
FROM employee
ORDER BY Salary DESC
LIMIT 1
And a standard one
WITH E AS
(
SELECT Name, Salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) RN /*Or RANK() for ties*/
FROM employee
)
SELECT Name, Salary FROM E WHERE RN=1
In case there are multiple rows with the same MAX number then you can just limit the number to desired number like this
SELECT Name, Salary FROM Minions
WHERE Salary = (SELECT Max(Salary) FROM Minions) LIMIT 1
Select e.name, e.salary from employee e where
not exists (select salary from employee e1 where e.salary < e1.salary)
This will of course return more than one record if there are multiple people with the max salary.
If you are using an oracle database and only want a single "employee" then:
SELECT MAX( name ) KEEP ( DENSE_RANK LAST ORDER BY salary ASC ) AS name,
MAX( salary ) KEEP ( DENSE_RANK LAST ORDER BY salary ASC ) AS salary
FROM Minions;
SQLFIDDLE
(kudos to Neil N for his table name)
SQL Server has a similar FIRST_VALUE (or LAST_VALUE) analytic function.
PostgreSQL also supports window functions including LAST_VALUE.
These type of queries (grouped operaions) can execute with sub query.
Example
select *from emp where sal=(max(sal)from emp)
Assuming only one employee has maximum salary
SQL Server:
select top 1 name, salary
from employee
order by salary desc
Oracle:
select name, salary
from employee
order by salary desc
limit 1
Above queries scans the table only once unlike other queries using subqueries.