Getting the third moving date - sql

I need to get the third moving date based on address for each person. Either status change or address change will make DT change. For example, for PERSON_ID:1, the third moving date should be 07/16/2016. Thanks!
The data is as below:
PERSON_ID STATUS DT ADDRESS
1 12 5/6/2016 3
1 6 5/8/2016 3
1 7 6/5/2016 3
1 1 6/13/2016 3
1 12 6/20/2016 1
1 17 7/8/2016 1
1 1 7/11/2016 1
1 12 7/16/2016 2
1 3 12/6/2016 2
2 5 3/11/2016 5
2 1 5/15/2016 4
2 6 7/18/2016 6
2 12 7/21/2016 6

Using row_number() and group by for the min(dt) per address:
Note: This will not work correctly if the person moves between the same addresses.
select
Person_id
, dt = convert(char(10),dt,120)
, Address
from (
select
person_id
, dt = min(dt)
, address
, rn = row_number() over (partition by person_id order by min(dt))
from t
group by person_id, address
) s
where rn = 3
rextester demo: http://rextester.com/VLTUU16478
returns:
+-----------+------------+---------+
| Person_id | dt | Address |
+-----------+------------+---------+
| 1 | 2016-07-16 | 2 |
| 2 | 2016-07-18 | 6 |
+-----------+------------+---------+
To solve this correctly for a person moving between the same addresses, you have to address the gaps and islands problem.
Adding an additional subquery to the above solution so we can identify and group by the islands:
select
Person_id
, dt = convert(char(10),dt,120)
, Address
from (
select
person_id
, dt = min(dt)
, address
, rn = row_number() over (partition by person_id order by min(dt))
from (
select
person_id
, address
, dt
, island = row_number() over (partition by person_id order by dt)
- row_number() over (partition by person_id, address order by dt)
from t
) s
group by person_id, address, island
) s
where rn = 3
rextester demo: http://rextester.com/PPIH49666
returns:
+-----------+------------+---------+
| Person_id | dt | Address |
+-----------+------------+---------+
| 1 | 2016-07-16 | 3 |
| 2 | 2016-07-18 | 5 |
+-----------+------------+---------+

I'm not sure i understood what you need to get but I think what you're trying to do is something like:
SELECT * FROM
(
SELECT person_id,adress,[status],DT, rank() over (partition by person_id order by adress,dt)-1 as movement
FROM #t
WHERE person_id=1
) t
WHERE t.movement=3

Question is not entirely clear
select PERSON_ID, STATUS, DT, ADDRESS
from ( select PERSON_ID, STATUS, DT, ADDRESS
, row_number() over (partition by person order by dt) as rn
from ( select PERSON_ID, STATUS, DT, ADDRESS
, row_number() over ( partition by PERSON_ID, address order by dt) as rn
from table
) tt
where tt.rn = 1
) rr
where rr.rn = 3

Related

SQL getting top 2 rows by date per PolicyId but with distinct dates

ValId | PolicyId | Date | Value
------+----------+------------+-------
1 | 11 | 2020-06-01 | 2000
2 | 11 | 2020-06-03 | 3000
3 | 11 | 2020-06-03 | 4000
4 | 12 | 2020-06-02 | 8000
5 | 12 | 2020-06-03 | 8500
I wanted to get top 2 latest Val rows for each PolicyId but they cannot be from the same date.
Rows for PolicyId = 12 are returned correctly - ValId 4 and 5.
For PolicyId = 11, rows with ValId 2 and 3 are returned but as they are on the same date I wanted row of ValId 1 to be returned instead of ValId 2.
SELECT
V.ValId, V.PolicyId, V.Value, V.Date
FROM
(SELECT
ValId, PolicyId, Value, Date,
ROW_NUMBER() OVER (PARTITION BY PolicyId ORDER BY Date Desc, ValId DESC) AS RowNum
FROM
TVal) V
WHERE
RowNum <= 2
You can enumerate the rows by dates and within dates:
select t.*
from (select t.*,
dense_rank() over (partition by policyid order by date desc valId desc) as seqnum,
rank() over (partition by policyid, date order by valId desc) as seqnum_within_date
from tval
) t
where seqnum <= 2 and seqnum_within_date = 1;
Using the suggestion from Gordon Linoff I was able to complete the sql as below
Select v.* from
(
select t.*,
row_number() over (partition by policyid order by date desc valId desc) as seqnum,
from (select t.*
dense_rank() over (partition by policyid, date order by valId desc) as seqnum_within_date
from tval
) t where seqnum_within_date = 1
)v where seqnum <= 2

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().

Get latest rows by date from aggregate

Hey i'm kinda stuck with this query. Using SQL-server
i have in the table, UNIQUE(date, medId, userId)
I have this table
date | medId | userId | Quantity
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
2016-06-06 | 1 | 1 | 10
i want to get the row with the max date, per group of medId,userId, in this case
i would get
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
thanks in advance!
i've tried this
SELECT
a.userMedStockDate,
a.userMedStockMedId,
a.userMedStockUserId,
a.userMedStockQuantity
FROM (SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER() OVER (partition by userMedStockMedId,userMedStockUserId
ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY
userMedStockUserId,
userMedStockQuantity,
userMedStockMedId) a
WHERE a.rnk = 1
[SOLVED]
this should work
select * from
(
select
[date] , medId, userId ,Quantity
,row_number() over (partition by medId, userId order by [date] desc) as rowid
from yourtable
) as x
where rowid = 1
Could also try this:
select y.* from
table1 y inner join
(
SELECT [Date] = MAX([Date]), medId, userId
FROM table1
GROUP BY medId, userId
) x on y.[Date] = x.[Date] and y.medId = x.medId and y.userId = x.userId
i changed the fields to my actual table but here
SELECT
a.userMedStockDate, a.userMedStockMedId, a.userMedStockUserId, a.userMedStockQuantity
FROM(
SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER()OVER(partition by userMedStockMedId, userMedStockUserId ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY userMedStockUserId, userMedStockQuantity, userMedStockMedId
) a
WHERE a.rnk = 1

Oracle SQL: Transform rows to multiple columns

I'm using Oracle 11G and need a way to turn rows into new groups of columns in a select statement. We're transitioning to a 1:3 relationship for some of our data and need a way to get it into a view. Can you help us transform data that looks like this:
+---------+------------+
| User_Id | Station_Id |
+---------+------------+
| 1 | 203 |
| 1 | 204 |
| 2 | 203 |
| 3 | 487 |
| 3 | 3787 |
| 3 | 738 |
+---------+------------+
into this:
+---------+-------------+-------------+---------------+
| User_Id | Station_One | Station_Two | Station_Three |
+---------+-------------+-------------+---------------+
| 1 | 203 | 204 | Null |
| 2 | 203 | Null | Null |
| 3 | 487 | 3787 | 738 |
+---------+-------------+-------------+---------------+
Let me know what ever other specifics you would like and thank you for any help you can give!
You can use row_number and self joins:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct c1.userid,
c1.stationid station_one,
c2.stationid station_two,
c3.stationid station_three
from cte c1
left join cte c2 on c1.userid=c2.userid and c2.rn=2
left join cte c3 on c1.userid=c3.userid and c3.rn=3
where c1.rn=1
See the demo
You can also do it with row_number and subqueries:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct userid,
(select stationid from cte c where c.userid=cte.userid and c.rn=1) station_one,
(select stationid from cte c where c.userid=cte.userid and c.rn=2) station_two,
(select stationid from cte c where c.userid=cte.userid and c.rn=3) station_three
from cte
See the demo
The easiest way to accomplish this in my experience is to use conditional aggregation:
WITH mydata AS (
SELECT 1 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 1 AS user_id, 204 AS station_id FROM dual
UNION ALL
SELECT 2 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 487 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 3787 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 738 AS station_id FROM dual
)
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mydata
) GROUP BY user_id;
Just replace the mydata CTE in the above query with whatever your table's name is:
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mytable
) GROUP BY user_id;

Selecting most recent record for each group with a sum function

This is a sample table
ID | Serial | Quantity | Date_Created
-------------------------------------
1 | AS1GD | 10 | 2014-12-25 8:00:00 AM
1 | GO9A4 | 5 | 2014-12-28 9:04:32 AM
2 | JF8WS | 15 | 2014-12-29 9:23:43 AM
2 | JFLE0 | 15 | 2015-01-04 10:53:12 AM
2 | S8A4A | 10 | 2015-01-05 9:12:46 AM
3 | FXOE3 | 20 | 2015-01-03 9:31:52 AM
3 | LSOR9 | 22 | 2015-01-06 12:00:44 PM
My expected result
ID | Serial | Total_Quantity | Last_DateCreated
-------------------------------------------------
1 | GO9A4 | 15 | 2014-12-28 9:04:32 AM
2 | S8A4A | 40 | 2015-01-05 9:12:46 AM
3 | LSOR9 | 42 | 2015-01-06 12:00:44 PM
Here's a query I tried but it's not returning the sum but only the quantity of the record
WITH total AS
( SELECT [ID], [date_created], [serial], sum(quantity) as qty,
ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM [table]
group by ID, date_created, serial
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
Since you are grouping by more than the ID but want the SUM() at the ID level, you can add OVER() to your SUM():
;WITH total AS ( SELECT [ID]
, [date_created]
, [serial]
, SUM(SUM(quantity)) OVER(PARTITION BY [ID]) as qty
, ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM [table]
GROUP BY ID, date_created, serial
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
The above creates an oddity in which you need two SUM() in order to use the OVER(), but you can ditch the GROUP BY altogether in your example:
;WITH total AS ( SELECT [ID]
, [date_created]
, [serial]
, SUM(quantity) OVER(PARTITION BY [ID]) as qty
, ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM Table1
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
Demo: SQL Fiddle
This will work as long as you don't have two records with the same ID created in the same second:
WITH RecentSUM AS
(
SELECT ID, MAX(DateCreated) DateCreated, SUM(Quantity) TotalQuantity
FROM [table]
GROUP BY ID
)
SELECT t.ID, t.Serial, r.TotalQuantity, r.DateCreated
FROM RecentSUM r
INNER JOIN [table] t ON t.ID = r.ID and t.DateCreated=r.DateCreated;