Get latest 2 records from table - sql

I have a SQL Server table like this
ProdID Code
-------- ------
1001 A
2001 B
1001 C
3001 D
3001 E
1001 F
1001 Z
2001 G
2001 H
3001 I
4001 J
I am expecting output as
ProdID Code
-------- ------
1001 Z
1001 F
2001 H
2001 G
3001 I
3001 E
Only to show latest 2 data. If any id has less than 2 data I don't want to show it (like ProdID 4001).

Try this
;With cte As
(Select ProdID, Code, Row_Number() Over(Partition By ProdID Order By Code Desc) As rn,
Count(*) Over(Partition By ProdID) As NbrRows
From mytable)
Select ProdID, Code
From cte
Where rn <= 2 And NbrRows > 1
Order By ProdID, Code desc;
sql fiddle demo

Using ROW_NUMBER and COUNT:
SQL Fiddle
;WITH cte AS(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY ProdID ORDER BY Code DESC),
cnt = COUNT(*) OVER(PARTITION BY ProdID)
FROM tbl
)
SELECT
ProdID, Code
FROM cte
WHERE
rn <= 2
AND cnt >= 2

Related

DB2 Toad SQL - Group by Certain Columns using Max Command

I am having some trouble with the below query. I do understand I need to group by ID and Category, but I only want to group by ID while keeping the rest of the columns based on Rank being max. Is there a way to only group by certain columns?
select ID, Category, max(rank)
from schema.table1
group by ID
Input:
ID Category Rank
111 3 4
111 1 5
123 5 3
124 7 2
Current Output
ID Category Rank
111 3 4
111 9 1
123 5 3
124 7 2
Desired Output
ID Category Rank
111 1 5
123 5 3
124 7 2
You can use:
select *
from table1
where (id, rank) in (select id, max(rank) from table1 group by id)
Result:
ID CATEGORY RANK
---- --------- ----
111 1 5
123 5 3
124 7 2
Or you can use the ROW_NUMBER() window function. For example:
select *
from (
select *,
row_number() over(partition by id order by rank desc) as rn
from table1
) x
where rn = 1
See running example at db<>fiddle.
You can try using - row_number()
select * from
(
select ID, Category,rank, row_number() over(partition by id order by rank desc) as rn
from schema.table1
)A where rn=1

How can I select an ID each month with the highest Mark

I am fairly new to SQL. My table is
id mark datetimes
------|-----|------------
1001 | 10 | 2011-12-20
1002 | 11 | 2012-01-10
1005 | 12 | 2012-01-10
1003 | 10 | 2012-01-10
1004 | 11 | 2018-10-10
1006 | 12 | 2018-10-19
1007 | 13 | 2018-03-12
1008 | 15 | 2018-03-13
I need to select an ID with the highest mark at the end of each month (Year also matters) and ID can be repeated
My desired output would be
id mark
-----|----
1001 | 10
1005 | 12
1006 | 12
1008 | 15
So far I've Only able to get the highest value in each month
Select Max(Mark)'HighestMark'
From StudentMark
Group BY Year(datetimes), Month(datetimes)
When I tried to
Select Max(Mark)'HighestMark', ID
From StudentMark
Group BY Year(datetimes), Month(datetimes), ID
I get
Id HighestMark
----------- ------------
1001 10
1002 11
1003 12
1004 10
1005 11
1006 12
1007 13
1008 15
You can try like following.
Using ROW_NUMBER()
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY YEAR(DATETIMES)
,MONTH(DATETIMES) ORDER BY MARK DESC) AS RN
FROM [MY_TABLE]
)T WHERE RN=1
Using WITH TIES
SELECT TOP 1 WITH TIES ID, mark AS HighestMarks
FROM [MY_TABLE]
ORDER BY ROW_NUMBER() OVER (PARTITION BY YEAR(datetimes)
,MONTH(datetimes) ORDER BY mark DESC)
Example:
WITH MY AS
(
SELECT
* FROM (VALUES
(1001 , 10 , '2011-12-20'),
(1002 , 11 , '2012-01-10'),
(1005 , 12 , '2012-01-10'),
(1003 , 10 , '2012-01-10'),
(1004 , 11 , '2018-10-10'),
(1006 , 12 , '2018-10-19'),
(1007 , 13 , '2018-03-12'),
(1008 , 15 , '2018-03-13')
) T( id , mark , datetimes)
)
SELECT ID,Mark as HighestMark FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY YEAR(DATETIMES),MONTH(DATETIMES) ORDER BY MARK DESC) AS RN
FROM MY
)T WHERE RN=1
Output:
ID HighestMark
1001 10
1005 12
1008 15
1006 12
I don't see a way of doing this in a single query. But we can easily enough use one subquery to find the final mark in the month for each student, and another to find the student with the highest final mark.
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ID, CONVERT(varchar(7), datetimes, 126)
ORDER BY datetimes DESC) rn
FROM StudentMark
)
SELECT ID, Mark AS HighestMark
FROM
(
SELECT *,
RANK() OVER (PARTITION BY CONVERT(varchar(7), datetimes, 126)
ORDER BY Mark DESC) rk
FROM cte
WHERE rn = 1
) t
WHERE rk = 1
ORDER BY ID;
Demo
In below query you have included ID column for Group By, because of this, it is considering all data for all ID.
Select Max(Mark)'HighestMark', ID From StudentMark Group BY Year(datetimes), Month(datetimes), ID
Remove ID column from this script and try again.
Use RANK in case there are more than 1 student having the same highest mark.
select id, mark
from
(select *,
rank() over( partition by convert(char(7), datetimes, 111) order by mark desc) seqnum
from studentMark ) t
where seqnum = 1
this should work:
select s.ID, t.Mark, t.[Month year] from Studentmark s
inner join (
Select
Max(Mark)'HighestMark'
,cast(Year(datetimes) as varchar(10)) +
cast(Month(datetimes) as varchar(10)) [month year]
From StudentMark
Group BY cast(Year(datetimes) as varchar(10))
+ cast(Month(datetimes) as varchar(10))) t on t.HighestMark = s.mark and
t.[month year] = cast(Year(s.datetimes) as varchar(10)) + cast(Month(s.datetimes) as varchar(10))
If for some reason you abhor subqueries, you can actually do this as:
select distinct
first_value(id) over (partition by year(datetimes), month(datetime) order by mark desc) as id
max(mark) over (partition by year(datetimes), month(datetime))
from StudentMark;
Or:
select top (1) with ties id, mark
from StudentMark
order by row_number() over (partition by year(datetimes), month(datetime) order by mark desc);
In this case, you can get all students in the event of ties by using rank() or dense_rank() instead of row_number().

Oracle Remove Consecutive duplicates

I have table in which I store the evauation results of customer. Evaluation can be triggered multiple times. Below is the sample data
CUSTOMER_EVAL_RESULTS:
SEQ CUSTOMER_ID STATUS RESULT
1 100 C XYZ
3 100 C XYZ
7 100 C ABC
8 100 C PQR
11 100 C ABC
12 100 C ABC
From above data set I want only the rows with SEQ as 1,7,8,11.
I used below query suggested on other links but it is not giving the desired result. Please help
SELECT * FROM (
SELECT E.*, ROW_NUMBER() OVER(PARTITION BY CUSTOMER_ID, STATUS, RESULT ORDER BY SEQ) ROW_NUM
FROM CUSTOMER_EVAL_RESULTS E WHERE E.CUSTOMER_ID=100
) WHERE ROW_NUM=1;
You can utilize LAG to check the previous row's value:
SELECT *
FROM
(
SELECT E.*,
LAG(RESULT)
OVER(PARTITION BY CUSTOMER_ID, STATUS
ORDER BY SEQ) prevResult
FROM CUSTOMER_EVAL_RESULTS E
WHERE E.CUSTOMER_ID=100
)
WHERE prevResult IS NULL
OR prevResult <> RESULT
Please try the below
select * from CUSTOMER_EVAL_RESULTS
where not exists (select 1 from CUSTOMER_EVAL_RESULTS
a,CUSTOMER_EVAL_RESULTS b
where a.seq_no < b.seq_no and a.customer_id=b.customer_id
and a.status=b.status and a.result=b.result
and not exists(select 1 from CUSTOMER_EVAL_RESULTS c
where a.seq_no < c.seq_no and c.seq_no < b.seq_no ));

Need SQL to get top row

I'm using a SQL Server database and have this data:
Loc dept deptdesc
-----------------------
1 201 ccccc
1 201 fffff
1 201 uuu
2 202 lllll
3 203 ooo
3 203 yyy
3 203 mmm
3 203 bbbb
I need help with the SQL query to get data:
Loc dept deptdesc
----------------------------
1 201 ccccc
2 202 lllll
3 203 ooo
You stated in your comments that it can be any of the descriptions, assuming this is true a simple group by will work.
SELECT Loc, dept, MIN(deptdesc)
FROM YourTable
GROUP BY Loc, dept
You can do this using Row_Number() and only taking the first of each group.
;With Cte As
(
Select *, Row_Number() Over (Partition By Dept Order By (Select Null)) As RN
From YourTable
)
Select Loc, Dept, DeptDesc
From Cte
Where RN = 1
Use the below query.. You can use CTE.
WITH cte_1
AS
( Select Loc,Dept,DeptDesc
,Row_number()over(partition by Loc,Dept Order by (select 1)) as RNO
From YourTable)
Select Loc,Dept,DeptDesc
From cte_1
Where RNO =1
Have assumed there is a surrogate id
WITH firstVal AS(
SELECT
DISTINCT first_value(column4) OVER(PARTITION BY column1 ORDER BY column2 ) AS id
FROM
(VALUES
(1,201,'ccccc',100)
,(1,201,'fffff',101)
,(1,201,'uuu',102),
(2,202,'lllll',103),
(3,203,'ooo',104),
(3,203,'yyy',105),
(3,203,'mmm',106),
(3,203,'bbbb',107)
)
)
SELECT
column1,column2,column3
FROM
(VALUES
(1,201,'ccccc',100)
,(1,201,'fffff',101)
,(1,201,'uuu',102),
(2,202,'lllll',103),
(3,203,'ooo',104),
(3,203,'yyy',105),
(3,203,'mmm',106),
(3,203,'bbbb',107)
) vals
INNER JOIN firstVal ON firstVal.id = vals.column4

SQL group by with NULL

I have a table something like this:
ID ProductID ProductName Price
== ========= =========== =====
1 XX1 TShirt 10
2 XX1 TShirt 10
3 NULL TShirt 10
4 XX2 Shirt 20
5 XX3 Shirt1 30
Now I want this to group by ProductName and results will be as follows
ID ProductID ProductName Price
== ========= =========== =====
1 XX1 TShirt 30
4 XX2 Shirt 20
5 XX3 Shirt1 30
Thanks
ProductID seems to be irrelevant for the group, so don't use it. To get all columns you could use a CTE and a ranking function like ROW_NUMBER:
WITH CTE AS(
SELECT ID,
ProductID,
ProductName,
Price = SUM(Price) OVER (PARTITION BY ProductName),
RN = ROW_NUMBER() OVER (PARTITION BY ProductName ORDER BY ID)
FROM dbo.TableName
)
SELECT CTE.* FROM CTE
WHERE RN = 1
If you want to take the row which contains the ProductID(where it is not NULL) modify the ORDER BY:
WITH CTE AS(
SELECT ID,
ProductID,
ProductName,
Price = SUM(Price) OVER (PARTITION BY ProductName),
RN = ROW_NUMBER() OVER (PARTITION BY ProductName
ORDER BY CASE WHEN ProductID IS NOT NULL
THEN 0 ELSE 1 END, ID)
FROM dbo.TableName
)
SELECT CTE.* FROM CTE
WHERE RN = 1