Dense_Rank with Case statement is not giving Rank with Date - sql

I have an issue while using Dense_Rank with CASE Statement. Below is the
sample table screenshot
So my requirement is two provide a Rank to every employee based on Emp_Dep_id
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when Emp_Joining_Date and Emp_Country is same
Below is the code to give rank
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)
Below is Output-->
So,I am facing two issues-
Why Rank is skipping if same ranks is there ex- after second rank why sixth rank is coming next
I want to give rank basis of Emp_Joining_Date Currently it is behaving like firstly it is assigning rank if Emp_Dep_Id is not null after that it is continuing for Emp_Dep_Id is null.
I want to get the rank based on latest Emp_Joining_Date means joining date with 2016 with null should come first
Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**
I hope this is going to help

I think you want this logic:
You want a single dense_rank(). The trick is to get the logic into the order by clause.
I think this is what you want:
Select dense_rank() over (order by Emp.Emp_Dep_Id,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Joining_Date end) desc,
(case when Emp.Emp_Dep_Id IS NULL then Emp.Emp_Country end) desc
)

The Dense rank is working as per the result set & the number of records in it. Please refer the link given
https://msdn.microsoft.com/en-IN/library/ms173825.aspx?
use the below query to know the result better. Also try to use order by after the SQL statement to order it properly.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country),
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc),
case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,
Emp.Emp_Joining_Date desc,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
from Employee Emp with (nolock)

Could you please use the below query whether your requirement is met or not. I believe the dense range will operate as your requirement. You don't need to add a extra case statement. Only thing you need to specify is the order of the column. It automatically handle as you required.
Select
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc,Emp.Emp_Joining_Date desc,Emp.Emp_Country) as rnk ,*
from Employee Emp with (nolock)
Req 1-->If Emp_Dep_id is same give same rank
Req 2-->If Emp_Dep_id is null then give same rank only when
Emp_Joining_Date and Emp_Country is same

Thanks Guys for your valuable response,I fixed my issue by doing this way
1. Step 1
Select case
when Emp.Emp_Dep_Id IS NULL
then
DENSE_RANK() over (order by Emp.Emp_Joining_Date,Emp.Emp_Country)
else
DENSE_RANK() over (order by Emp.Emp_Dep_Id desc)
end as
rnk ,*
into #Emp_Output_Tbl
from Employee Emp
Select * from #Emp_Output_Tbl order by Emp_Joining_date desc
--Step 2
Select distinct rnk,Emp_Joining_date into #Emp_New_Tbl from #Emp_Output_Tbl order by Emp_Joining_date desc
Select * from #Emp_New_Tbl order by Emp_Joining_Date desc
Select * from #Emp_Output_Tbl order by Emp_Joining_Date desc
--Step 3
Select * from #Emp_Output_Tbl where rnk in(
Select TOP 5 rnk from #Emp_New_Tbl
)
order by Emp_Joining_Date desc
**Output as per expectation**

Related

Filtering for MAX Beginning Date

I am currently getting the output of the image below. I want to be able to retrieve the latest Turn Time. Essentially the MAX beginning date and MAX end date. How Should I structure my query ?
I think you just want order by:
select top (1) t.*
from t
order by enddate desc, beginning_date desc;
If you want this per id, then you can use window functions or top (1) with ties:
select top (1) t.*
from (select t.*,
row_number() over (partition by id order by enddate desc, beginning_date desc) as seqnum
from t
) t
where seqnum = 1;
You can use row_number()
select * from
(
select *,row_number() over(parition by id order by beginningdate desc) as rn
from tablename
)A where rn=1
For the larger turn time -
select * from
(
select *,row_number() over(parition by id order by turntime desc) as rn
from tablename
)A where rn=1

Oracle optimise SQL query - Multiple Max()

I have a table where first I need to select data by max(event_date) then need to
filter the data by max(event_sequence) then filter again by max(event_number)
I wrote following query which works but takes time.
Here the the query
SELECT DISTINCT a.stuid,
a.prog,
a.stu_prog_id,
a.event_number,
a.event_date,
a.event_sequence,
a.prog_status
FROM table1 a
WHERE a.event_date=
(SELECT max(b.event_date)
FROM table1 b
WHERE a.stuid=b.stuid
AND a.prog=b.prog
AND a.stu_prog_id=b.stu_prog_id)
AND a.event_seq=
(SELECT max(b.event_sequence)
FROM table1 b
WHERE a.stuid=b.stuid
AND a.prog=b.prog
AND a.stu_prog_id=b.stu_prog_id
AND a.event_date=b.event_date)
AND a.event_number=
(SELECT max(b.event_number)
FROM table1 b
WHERE a.stuid=b.stuid
AND a.prog=b.prog
AND a.stu_prog_id=b.stu_prog_id
AND a.event_date=b.event_date
AND a.event_sequence=b.event_sequence
I was wondering is there there a faster way to get the data?
I am using Oracle 12c.
You could try rephrasing your query using analytic functions:
SELECT
stuid,
prog,
stu_prog_id,
event_number,
event_date,
event_sequence,
prog_status
FROM
(
SELECT t.*,
RANK() OVER (PARTITION BY studio, prog, stu_prog_id
ORDER BY event_date DESC) rnk1,
RANK() OVER (PARTITION BY studio, prog, stu_prog_id, event_date
ORDER BY event_sequence DESC) rnk2,
RANK() OVER (PARTITION BY studio, prog, stu_prog_id, event_date, event_sequence
ORDER BY event_number DESC) rnk3
FROM table1 t
) t
WHERE rnk1 = 1 AND rnk2 = 1 AND rnk3 = 1;
Note: I don't actually know if you really need all three subqueries there. Adding sample data to your question might help someone else improve upon the solution I have given above.
I think you want a simple row_number() or rank():
select t1.*
from (select t1.*,
rank() over (partition by stuid, prog, stu_prog_id
order by event_date desc, event_sequence desc, event_number desc
) as seqnum
from table1 t1
) t1
where seqnum = 1;
If you have multiple records with EVENT_DATE, EVENT_SEQUENCE, EVENT_NUMBER as max respectively then in Tim's solution, Use DENSE_RANK or use the following to fetch the exact max and compare with original column data.
SELECT DISTINCT
A.STUID,
A.PROG,
A.STU_PROG_ID,
A.EVENT_NUMBER,
A.EVENT_DATE,
A.EVENT_SEQUENCE,
A.PROG_STATUS
FROM
(
SELECT
A.STUID,
A.PROG,
A.STU_PROG_ID,
A.EVENT_NUMBER,
A.EVENT_DATE,
A.EVENT_SEQUENCE,
A.PROG_STATUS,
MAX(A.EVENT_DATE) OVER(
PARTITION BY A.STUID, A.PROG, A.STU_PROG_ID
) AS MAX_EVENT_DATE,
MAX(A.EVENT_SEQUENCE) OVER(
PARTITION BY A.STUID, A.PROG, A.STU_PROG_ID, A.EVENT_DATE
) AS MAX_EVENT_SEQUENCE,
MAX(A.EVENT_NUMBER) OVER(
PARTITION BY A.STUID, A.PROG, A.STU_PROG_ID, A.EVENT_DATE, A.EVENT_SEQUENCE
) AS MAX_EVENT_NUMBER
FROM
TABLE1 A
) A
WHERE
A.MAX_EVENT_DATE = A.EVENT_DATE
AND A.MAX_EVENT_SEQUENCE = A.EVENT_SEQUENCE
AND A.MAX_EVENT_NUMBER = A.EVENT_NUMBER;
Cheers!!
As being an Oracle 12c user, you can use
[ OFFSET offset { ROW | ROWS } ]
[ FETCH { FIRST | NEXT } [ { rowcount | percent PERCENT } ]
{ ROW | ROWS } { ONLY | WITH TIES } ]
syntax as :
SELECT DISTINCT a.stuid,
a.prog,
a.stu_prog_id,
a.event_number,
a.event_date,
a.event_sequence,
a.prog_status
FROM table1 a
ORDER BY event_date DESC, event_sequence DESC, event_number DESC
FETCH FIRST 1 ROW ONLY;
where WITH TIES clause is not needed for your case, since you're looking for DISTINCT rows, and OFFSET is not needed either, since starting point is just the beginning of a descendingly ordered columns. Even, using the keyword ROW as ROWS is optional, even for the case of plural rows such as FETCH FIRST 5 ROW ONLY;
^^ --> ROWS without S
Demo

Select most recent status for each ID and department code

I have the following table:
I want to get the most recent status for each dept_code that a CL_ID has. So the desired output would be this:
I have tried the following but this give me just the most recent status for each client and not each of their dept_codes.
SELECT *
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT] C
INNER JOIN
(SELECT CLIENT_NUMBER, MAX(STATUS_DATE) AS SDATE
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT]
GROUP BY CLIENT_NUMBER) X
ON X.CLIENT_NUMBER = C.CLIENT_NUMBER
AND X.SDATE = C.STATUS_DATE
ORDER BY C.CLIENT_NUMBER
Any help would be much appreciated. Thanks.
A convenient method that works in SQL Server is:
select top (1) cl.*
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl
order by row_number() over (partition by cl_id, dept_code order by status_date desc);
A method that is efficient with the right indexes in almost any database is:
select cl.*
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl
where cl.status_date = (select max(cl2.status_date)
from [CIMSHR6_MERGED].[dbo].[C3CLSTAT] cl2
where cl2.cl_id = cl.cl_id and cl2.dept_code = cl.dept_code
);
The right index is on (cl_id, dept_code, status_date).
I would also use ROW_NUMBER, but with a subquery:
SELECT CL_ID, Status_date, Status, Dept_code
FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CL_ID, Dept_code ORDER BY Status_date DESC) rn
FROM CIMSHR6_MERGED].[dbo].[C3CLSTAT]
) t
WHERE rn = 1;
1) Firstly group everything on Dept_Code,CL_ID and assign rank for each row with in the group in descending order.
2) Select all the rows with rnk=1 which would display your desired result.
SELECT Z.CL_ID,
Z.Status_Date,
Z.Status,
Z.Dept_Code
FROM
(
SELECT *,
RANK() OVER( PARTITION BY Dept_Code,CL_ID, ORDER BY Status_Date DESC ) AS rnk
FROM [CIMSHR6_MERGED].[dbo].[C3CLSTAT]
) Z
WHERE Z.rnk = 1;
This would work for almost all databases
select * from c3clstat c
where exists
(select 1 from c3clstat c1
where c1.cl_id=c.cl_id
and c1.dept_code=c.dept_code
group by cl_id,dept_code
having c.status_date=max(c1.status_date)
)

How to sort a same column both in ASC & DESC order without duplicates

With an SQL Query how do we get the output of 2 columns, the first one being a column sorted in ASC order and the second one with the order DESC without any duplicate values and both are same columns.
EMP table:
EMP_NAME
Megha
Vicky
Neha
Sachin
Neha
The query output should be
EMP_NAME_ASC EMP_NAME_DESC
Megha Vicky
Neha Sachin
Sachin Neha
Vicky Megha
I tried below solution
How to sort a same column both in asc order and desc order
but that is not giving me distinct values.
Any help would be appreciated.
Thank you in advance.
With a little modification on Gordon Linoff's answer, you can get distinct names:
;WITH
cte AS
(
SELECT EMP_NAME
, DENSE_RANK() OVER (ORDER BY EMP_NAME ASC) AS RankAsc
, DENSE_RANK() OVER (ORDER BY EMP_NAME DESC) AS RankDesc
, ROW_NUMBER() OVER (PARTITION BY EMP_NAME ORDER BY EMP_NAME)
AS RowNumber
FROM #EMP
)
SELECT cte1.EMP_NAME AS EMP_NAME_ASC
, cte2.EMP_NAME AS EMP_NAME_DESC
FROM cte cte1
INNER JOIN cte cte2 ON cte1.RankAsc = cte2.RankDesc
WHERE cte1.RowNumber = 1
AND cte2.RowNumber = 1
The ROW_NUMBER gives a sequential number each time the same name appears. The first time Neha appears it will get 1, the second time it will get 2 and so on. We are interested in the first time each name appears and hence get the distinct names only.
You are two separate lists in columns. This is not a SQL-ish way to store data -- the rows don't mean anything. But you can do this, using row_number():
select a.name, d.name
from (select name, row_number() over (order by name asc) as seqnum
from t
) a join
(select name, row_number() over (order by name desc) as seqnum
from t
) d
on a.seqnum = d.seqnum;

SQL Server Partition Order - No tie DenseRank values even if rows are same

This question is best explained with an image and the script I have currently... How can I extract a FULL one row per assignment, with the lowest rank, and if there are 2 rows with a denserank as 1, then choose either of them?...
select *
,Dense_RANK() over (partition by [Assignment] order by [Text] desc) as
[DenseRank]
from [dbo].[CLEANSED_T3B_Step1_Res_Withdups____CP]
select * from
(
select *
,Dense_RANK() over (partition by [Assignment] order by [Text] desc, NewID()
) as [DenseRank] from [dbo].[CLEANSED_T3B_Step1_Res_Withdups____CP]
) as A
where A.[DenseRank] = 1
Second script is working perfectly!
SELECT * INTO
[dbo].[CLEANSED_T3B_Step1_COMPLETED]
from
(
select *
,Dense_RANK() over (partition by [Assignment] order by
left([Text],1) desc , [Diff_Doc_Clearing_Date] desc , [Amount] asc
as [DenseRank]
from [dbo].[CLEANSED_T3B_Step1_Res_Withdups____CP]
)
as A
where A.[DenseRank] = 1
No longer need just a random first Tied '1st place', now need to get the one with the highest day diff and then also the highest amount after. SO have adapted everything in this version 3.
It seems you don't want to use DENSE_RANK but ROW_NUMBER.
with cte as(
select t.*, rn = row_number() over(partition by assignment order by [text] desc)
from tablename t
)
select * from cte
where rn = 1
Order by 'newid()' as the 'tie-breaker'
Order by [Text],Newid()