Ascending and Descending according to the column value - sql

I have a EMPLOYEE table with EMP_ID,NAME and DEPARTMENT_ID.
I want to order all the record with odd DEPARTMENT_ID as ASC and even DEPARTMENT_ID as DESC.
Can it be done?
Thank you

You can use CASE in the ORDER BY, change the sign accordingly:
...
ORDER BY CASE WHEN DEPARTMENT_ID % 2 = 0
THEN -DEPARTMENT_ID
ELSE DEPARTMENT_ID END ASC;

Try this
SELECT *
FROM EMPLOYEE
ORDER BY CASE WHEN DEPARTMENT_ID % 2 = 1 then 1 else 2 end,DEPARTMENT_ID

Makes two queries, first one filters odds, second pairs. Order by as you wish and then Union queries.
SELECT e.* FROM (SELECT *
FROM EMPLOYEE
WHERE MOD(DEPARTMENT_ID, 2) = 1
ORDER BY DEPARTMENT_ID ASC) e
UNION ALL
SELECT e1.* FROM (SELECT *
FROM EMPLOYEE
WHERE MOD(DEPARTMENT_ID, 2) = 0
ORDER BY DEPARTMENT_ID DESC) e1

Related

How to use group by, order by and where condition in sql?

Given table contains
employee_id,
Month_Year,
Salary,
City
Write a query to get fifth highest yearly paid employee from every city.
my code:
select * from (select * from(select * from (select * from dummy order by sal desc) where rownum <= 5) order by sal) where rownum <=1 ;
but this gives only the 5th highest in the total table.
where am i suppose to use group by statement ?
Thanks in advance.
You can try below -
select *
from tablename as t1
where
t1.salary = (select salary from tablename t2
where
t2.city= t1.city ORDER by salary desc limit 1 offset 5)
The following query is working for me. (number(4) in where condition is 'N' where 'N' is nth highest salary. In your case you want 5th highest salary so N-1 is 4)
find nth highest salary in sql
select salary,city
FROM tablename n1
WHERE (4) = (
SELECT COUNT(DISTINCT(n2.Salary))
FROM tablename n2
WHERE n2.Salary > n1.Salary and n1.city=n2.city group by n2.city)

Second Highest Salary

Write a SQL query to get the second highest salary from the Employee table.
| Id | Salary |
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
For example, given the above Employee table, the query should return 200 as the second highest salary. If there is no second highest salary, then the query should return null.
| SecondHighestSalary |
| 200 |
This is a question from Leetcode, for which I entered the following code:
SELECT CASE WHEN Salary = ''
THEN NULL
ELSE Salary
END AS SecondHighestSalary
FROM (SELECT TOP 2 Salary
,ROW_NUMBER() OVER (ORDER BY Salary DESC) AS Num
FROM Employee
ORDER BY Salary DESC) AS T
WHERE T.Num = 2
It says that the query does not return NULL if there's no value for second highest salary.
For eg. if the table is
| Id | Salary|
| 1 | 100 |
The query should return
|SecondHighestSalary|
| null |
and not
|SecondHighestSalary|
| |
Solution to the Leetcode 2nd highest salary problem is:
Select
Max(Salary) AS SecondHighestSalary
from Employee
where Salary < (
Select Max(Salary) from Employee
);
In case of ties you want the second highest distinct value. E.g. for values 100, 200, 300, 300, you want 200.
So get the highest value (MAX(salary) => 300) and then get the highest value less than that:
select max(salary) from mytable where salary < (select max(salary) from mytable);
Using window functions, utilizing NTH_VALUE gives a clean answer
SELECT (
SELECT NTH_VALUE(Salary, 2) OVER(ORDER BY Salary DESC) AS SecondHighestSalary
FROM Employee
GROUP BY Salary
LIMIT 1 OFFSET 1 ) AS SecondHighestSalary
;
Detailed Break-Down:
The outer SELECT Statement is required to get NULL value incase a value was not found (second rank is not present for example, or table only has one row)
NTH_VALUE(Salary, 2) is basically saying, look at each group (in our case it divides the table based on groups of Salary) and for each group add a column that lists the second highest value, for every row within the same group from that new column, we want to pick the second most paid (so only second row)
NTH_VALUE() OVER(ORDER BY) is in ASC order by default, make sure you explicit the DESC order
NTH_VALUE() merely gives the order to the rows within each group (here Salary) incase of two similar salaries in the same salary group it will give them separate ranks (ex 1 and 2) even if they have same value in the same group, For this use GROUP BY () statement
Because NTH_VALUE() merely gives the order to the columns, based on a group USE LIMIT 1 to get just one value (top value) and OFFSET 1 (to make that top value our targeted second most paid)
you should be able to do that with OFFSET 1/FETCH 1:
https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
SELECT id, MAX(salary) AS salary
FROM employee
WHERE salary IN
(SELECT salary FROM employee MINUS SELECT MAX(salary)
FROM employee);
You can try above code to find 2nd maximum salary.
The above code uses MINUS operator.
For further reference use the below links
https://www.techonthenet.com/sql/minus.php
https://www.geeksforgeeks.org/sql-query-to-find-second-largest-salary/
You can use RANK() function to rank the values for Salary column, along with a CASE statement for returning NULL.
SELECT
CASE WHEN MAX(SalaryRank) = 1 THEN NULL ELSE Salary as SecondHighestSalary
FROM
(
SELECT *, RANK()OVER(ORDER BY Salary DESC) As SalaryRank
FROM Employee
) AS Tab
WHERE SalaryRank = 2
It would be better to use the DENSE_RANK() function so that ranks don't get skipped whenever there is a tie for a position.
I would use DENSE_RANK() & do LEFT JOIN with employee table :
SELECT t.Seq, e.*
FROM ( VALUES (2)
) t (Seq) LEFT JOIN
(SELECT e.*,
DENSE_RANK() OVER (ORDER BY Salary DESC) AS Num
FROM Employee e
) e
ON e.Num = t.Seq;
While you can use a CTE (from MSSQL 2005 or newer) or ROWNUMBER the easiest and more "portable" way is to just order by twice using a subquery.
select top 1 x.* from
(select top 2 t1.* from dbo.Employee t1 order by t1.Salary) as x
order by x.Salary desc
The requisite to show null when there's not a second bigger salary is a bit more tricky but also easy to do with a if.
if (select count(*) from dbo.Employee) > 1
begin
select top 1 x.* from
(select top 2 emp.* from dbo.Employee emp order by emp.Salary) as x
order by x.Salary desc
end
else begin
select null as Id, null as Salary
end
Obs:. OP don't said what to do when the second largest is a tie with the first but using this solution is a simple matter of using a DISTINCT in the IF subquery.
Here is the easy way to do this
SELECT MAX(Salary) FROM table WHERE Salary NOT IN (SELECT MAX(Salary) FROM table);
You can try this for getting n-th highest salary, where n = 1,2,3....(int)
SELECT TOP 1 salary FROM (
SELECT TOP n salary
FROM employees
ORDER BY salary DESC) AS emp
ORDER BY salary ASC
Hope this will help you. Below is one of the implementation.
create table #salary (salary int)
insert into #salary values (100), (200), (300)
SELECT TOP 1 salary FROM (
SELECT TOP 2 salary
FROM #salary
ORDER BY salary DESC) AS emp
ORDER BY salary ASC
drop table #salary
The output is here 200 as 300 is first highest, 200 is second highest and 100 is the third highest as shown below
salary
200
Here n is 2
Query:
CREATE TABLE a
([Id] int, [Salary] int)
;
INSERT INTO a
([Id], [Salary])
VALUES
(1, 100),
(2, 200),
(3, 300)
;
GO
SELECT Salary as SecondHighestSalary
FROM a
ORDER BY Salary
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
| SecondHighestSalary |
| ------------------: |
| 200 |
select case
when cnt>1 then SecondHighestSalary
else null end as SecondHighestSalary
from
(select top 1 Salary as SecondHighestSalary,
(select count(distinct Salary) from Employee) as cnt
from (
select distinct top 2 Salary
from Employee
order by Salary desc ) as sal
order by SecondHighestSalary asc) as b
Select salary from employees limit 1,1 ;
Very easy way to find second highest salary
select
case when max(salary) is null then null else max(salary) end SecondHighestSalary
from (
select salary , dense_rank() over (order by salary desc) as rn
from Employee
)r
where rn = 2
This code returns null when there is no second highest salary
You can use the union condition to handle the null case
SELECT Salary as "SecondHighestSalary" from Employee
WHERE Salary < (SELECT MAX(salary) FROM Employee )
UNION
(SELECT null)
ORDER BY 1 DESC
LIMIT 1;
You can use exists() together with If-else statements.
Query:
If exists(select distinct salary as SecondHighestSalary from employee
order by salary desc offset 1 row fetch first 1 row only)
select distinct salary as SecondHighestSalary from employee
order by salary desc offset 1 row fetch first 1 row only
else select null as SecondHighestSalary;
#Please check the below code#
SELECT TOP 1 secondhighestsalary
FROM (SELECT
CASE WHEN z.SalaryRank >1 THEN Salary
ELSE null END secondhighestsalary, SalaryRank
FROM
(SELECT salary, RANK() OVER(ORDER BY Salary DESC) As SalaryRank
FROM Employee GROUP BY salary )z
GROUP BY Salary, SalaryRank
)c
ORDER BY secondhighestsalary DESC
Explanation:
** SELECT salary, RANK() OVER(ORDER BY Salary DESC) As SalaryRank
FROM Employee GROUP BY salary**
above query is to provide rank to Salary column, group by clause will take care the duplicate values ..... next
**SELECT
CASE WHEN z.SalaryRank >1 THEN Salary
ELSE null END secondhighestsalary, SalaryRank
FROM
(SELECT salary, RANK() OVER(ORDER BY Salary DESC) As SalaryRank
FROM Employee GROUP BY salary )z **
next piece of code assign NULL against rank 1 and salary for greater than 1 rank.
So if you have only one row in the table and as per our question we need to display second highest salary if not display NULL, it will take care that situation.
At last we need to Order by DESC and take the Top 1 record.
SELECT max(Salary) as SecondHighestSalary from Employee where salary <>(SELECT max(salary) from Employee)

sql query to fetch top 3 salaries

emp_name |salary
---------------
A |12568
B |3000
C |7852
D |2568
E |9852
F |1598
G |8569
I want a sql query to fetch the lowest 3 salaried employees
If you are using Oracle 12c or later you can make your query simpler with fetch. Instead of writing inner queries like this.
SELECT *
FROM
(SELECT * FROM EMPLOYEES EMP ORDER BY EMP.SALARY ASC
)
WHERE ROWNNUM <= 3
You can combine them into a single query.
SELECT * FROM employees emp ORDER BY emp.salary ASC
FETCH FIRST 3 ROWS ONLY;
More Information on the syntax and construct is available here.
http://www.dba-oracle.com/t_offset_fet_first_rows_only.htm
ORACLE:
SELECT emp_name
FROM ( SELECT *
FROM employees e
ORDER BY e.salary ASC)
WHERE ROWNUM < 4
Good luck!
You can use top 3 to get three record after ordering them in descending or ascending order. I have SQL server syntax but you can have idea from this for you target DBMS.
For top three max salaries
Select top 3 emp_name, salary
order by salary desc
For top three minimum salaries
Select top 3 emp_name, salary
order by salary asc
USE ASC AND LIMIT
Select emp_name, salary FROM TABLE_NAME
order by salary ASC LIMIT 3;
You didn't specify your DBMS, so this is ANSI SQL:
select emp_name, salary
from (
select emp_name, salary,
dense_rank() over (order by salary) as rnk
from employees
) t
where rnk <= 3;
This will also deal with employees that have the same salary. So the result might be more then three rows if more then one of the employees with the lowest salary have the same salary.

conditional order by clause in sql

I have a query that should order the result in asc or desc depending upon a column value.
e.g.
if employee of type manager exists THEN order by joining_date, bith_date ASC
else if employee is developer THEN order by joining_date, birth_date DESC.
I would like to achieve something like below, but can't achieve that.
ORDER BY CASE WHEN employee_type = 'm'
THEN joining_date, birth_date ASC;
WHEN employee_type = 'd'
THEN joining_date, birth_date DESC;
Well I got the answer after some research.
We can add multiple columns in where clause conditionally as follows :
ORDER BY DECODE(employee_type, 'm', joining_date, birth_date, salary) ASC,
DECODE(employee_type, 'd', joining_date, birth_date, salary) DESC
This will order the result on the basis of employee_type.
I suspect you want something like this:
ORDER BY
employee_type DESC -- first all the managers, then the developers
-- and in every one of these two groups
, joining_date -- first order by joining date
, CASE WHEN employee_type = 'm' -- and then either by
THEN birth_date -- birth date ascending for managers
ELSE NULL
END -- or
, birth_date DESC ; -- birth date descending for the rest (devs)
The question is a little bit poor specified.
order the result in asc or desc depending upon a column value.
A column takes many values (as there are multiple rows).
Now, order by clause use an expression and order rows upon it.
That expression should be morphotropic(;))
So, assuming stardard oracle's employee schema, managers are:
select *
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
An workaround query may be:
Select e.id, e.name, e.birth_date,
case
when (select count(*)
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
) --existence of manager
> 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from emp A
order by 4;
That exexpresion is the case; Using a constant(subquery that decides there are managers) it changes values from positive to negative, that is, change the order direction.
UPDATE: with the details in the comments:
select id, name, birth_date emp_type
from (
Select id, name, birth_date, emp_type,
case when cnt_mgr > 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from(
Select e.id, e.name, e.birth_date, emp_type,
count(case when emp_type='M' then 1 else 0 end) over() as mgr_count
from emp A
where your_conditions
)
order by tricky_expression
)
where rownum=1;
If there is a manager in the company this query returns the oldest manager, otherwise - the youngest developer.
select
id, name, birth_date, emp_type
from emp
where
id = (select
max(id) keep (dense_rank first order by
decode(emp_type, 'M', 1, 'D', 2),
joining_date,
decode(emp_type, 'M', 1, 'D', -1) * (birth_date - to_date('3000','yyyy')))
from emp)

ORACLE sql query for getting top 3 salaries rownum greater than

I want to write a query to display employees getting top 3 salaries
SELECT *
FROM (SELECT salary, first_name
FROM employees
ORDER BY salary desc)
WHERE rownum <= 3;
But I dont understand how this rownum is calculated for the nested query
will this work or if it has problem ,request you to please make me understand:
SELECT *
FROM (SELECT salary, first_name
FROM employees
ORDER BY salary )
WHERE rownum >= 3;
I went through this link Oracle/SQL: Why does query "SELECT * FROM records WHERE rownum >= 5 AND rownum <= 10" - return zero rows ,but it again points to a link, which does not gives the answer
a_horse_with_no_name's answer is a good one,
but just to make you understand why you're 1st query works and your 2nd doesn't:
When you use the subquery, Oracle doesn't magically use the rownum of the subquery, it just gets the data ordered so it gives the rownum accordingly, the first row that matches criteria still gets rownum 1 and so on. This is why your 2nd query still returns no rows.
If you want to limit the starting row, you need to keep the subquery's rownum, ie:
SELECT *
FROM (SELECT * , rownum rn
FROM (SELECT salary, first_name
FROM employees
ORDER BY salary ) )sq
WHERE sq.rn >= 3;
But as a_horse_with_no_name said there are better options ...
EDIT: To make things clearer, look at this query:
with t as (
select 'a' aa, 4 sal from dual
union all
select 'b' aa, 1 sal from dual
union all
select 'c' aa, 5 sal from dual
union all
select 'd' aa, 3 sal from dual
union all
select 'e' aa, 2 sal from dual
order by aa
)
select sub.*, rownum main_rn
from (select t.*, rownum sub_rn from t order by sal) sub
where rownum < 4
note the difference between the sub rownum and the main rownum, see which one is used for criteria
The "rownum" of a query is assigned before an order by is applied to the result. So the rownumw 42 could wind up being the first row.
Generally speaking you need to use the rownum from the inner query to limit your overall output. This is very well explained in the manual:
http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns009.htm#i1006297
I prefer using row_number() instead, because you have much better control over the sorting and additionally it's a standard feature that works on most modern DBMS:
SELECT *
FROM (
SELECT salary,
first_name,
row_number() over (order by salary) as rn
FROM employees
)
WHERE rn <= 3
ORDER BY salary;
You should understand that the derived table in this case is only necessary to be able to apply a condition on the generated rn column. It's not there to avoid the "rownum problem" as the value of row_number() only depends on the order specifiy in the over(...) part (it is independent of any ordering applied to the query itself)
Note this would not return employees that have the same salary and would still fall under the top three. In that case using dense_rank() is probably more approriate.
if you want to select the people with the top 3 salaries.. perhaps you should consider using analytics.. something more like
SELECT *
FROM (
SELECT salary, first_name, dense_rank() over(order by salary desc) sal_rank
FROM employees
)
WHERE sal_rank <= 3
ie ALL people with the 3rd highest(ranked) salary amount(or more)
the advantage of this over using plain rownum is if you have multiple people with the same salary they will all be returned.
Easiest way to print 5th highest salary.
SELECT MIN(SALARY) FROM (SELECT SALARY FROM EMPLOYEES ORDER BY DESC) WHERE ROWNUM BETWEEN 1 AND 5
according to same if u want to print 3rd or 4th highest salary then just chage last value.(means instead of 5 use 3 or 4 you will get 3rd or 4th highest salary).
SELECT MIN(SALARY) FROM (SELECT SALARY FROM EMPLOYEES ORDER BY DESC) WHERE ROWNUM BETWEEN 1 AND 4
SELECT MIN(SALARY) FROM (SELECT SALARY FROM EMPLOYEES ORDER BY DESC) WHERE ROWNUM BETWEEN 1 AND 3
SELECT EMPNO,
SAL,
(SELECT SUM(E.SAL) FROM TEST E WHERE E.EMPNO <= T.EMPNO) R_SAL
FROM (SELECT EMPNO, SAL FROM TEST ORDER BY EMPNO) T
Easiest way to find the top 3 employees in oracle returning all fields details:
SELECT *
FROM (
SELECT * FROM emp
ORDER BY sal DESC)
WHERE rownum <= 3 ;
select *
from (
select emp.*,
row_number() over(order by sal desc)r
from emp
)
where r <= 3;
SELECT Max(Salary)
FROM Employee
WHERE Salary < (SELECT Max(salary) FROM employee WHERE Salary NOT IN (SELECT max(salary) FROM employee))
ORDER BY salary DESC;