I want to get minimum of starttime of each id. But I'm not able to achieve it using min function. How to solve it? - sql

Table:
id | starttime | grade
-------------------------
1 | 4PM | A
1 | 5PM | C
2 | 2PM | A
2 | 3PM | B
In output I should get all the ids that have minimum starttime with all the columns.
For the above table output should be like this:
id | starttime | grade
-------------------------
1 | 4PM | A
2 | 2PM | A

Using top with ties and row_number() to get the minimum starttime for each id.
select top (1) with ties *
from t
order by row_number() over (partition by id order by starttime);
rextester demo: http://rextester.com/RJVT1405
returns:
+----+-----------+-------+
| id | starttime | grade |
+----+-----------+-------+
| 1 | 4PM | A |
| 2 | 2PM | A |
+----+-----------+-------+

In SQL Server, use top with ties:
select top (1) with ties t.*
from t
order by starttime;
TOP (1) only returns one row. TOP (1) WITH TIES returns all rows that have the same key value as in the first row specified by the ORDER BY.

You can use group by:
with minTimes (id, startTime) as
(
select id, min(startTime) from myTable
group by id
)
select t1.*
from myTable t1
inner join minTimes t2 on t1.id = t2.id and t1.startTime = t2.startTime

;WITH cte(id,starttime, grade)
AS
(
SELECT 1 , '4PM','A' Union all
SELECT 1 , '5PM','C' Union all
SELECT 2 , '2PM','A' Union all
SELECT 2 , '3PM','B'
)
,Final AS (
SELECT *
,Row_Number() OVER (
PARTITION BY MinStartTime ORDER BY MinStartTime
) Seq
FROM (
SELECT id
,CAST(starttime AS TIME) AS starttime
,MIN(CAST(starttime AS TIME)) OVER (
PARTITION BY ID ORDER BY starttime
) AS MinStartTime
,grade
FROM cte
) Dt
)
SELECT id
,CONVERT(VARCHAR(15), MinStartTime, 100) AS starttime
,grade
FROM final
WHERE Seq = 1
ORDER BY 1

Related

Recursive CTE sql query for calculating Rolling returns

My first time writing a recursive CTE in SQL to calculate the rolling returns.
Formula : 100*(1+Returns) for first value, and then (RollingReturns)*(1+Returns)
The table is as below:
+----+--------+--------+----------------+
| ID | Date | Return | RollingReturns |
+----+--------+--------+----------------+
| 1 | 1/1/20 | 0.50% | 100.5 |
| 1 | 1/2/20 | 1.00% | 101.51 |
| 1 | 1/3/20 | -0.7% | 100.74 |
| 1 | 1/4/20 | 0.50% | 101.25 |
+----+--------+--------+----------------+
My attempt at writing the SQL query:
WITH rn_cte AS (
SELECT ROW_NUMBER() OVER (ORDER BY DATE ASC) AS RN, DATE
FROM TABLE WHERE ID = 1
ORDER BY RN
)
rr_cte
AS
(
SELECT RN,P.DATE,RETURNS,RETURNS AS ROLLINGRETURNS
FROM TABLE P
JOIN rn_cte ON rn_cte.DATE = p.DATE
WHERE P.ID = 1 AND RN = 1
UNION ALL
SELECT RN,pm.DATE,pm.RETURNS,(rr_cte.ROLLINGRETURNS)*(1+pm.RETURNS) AS ROLLINGRETURNS
FROM TABLE pm WHERE pm.ID = 1
JOIN rr_cte ON rr_cte.RN = pm.RN+1
ORDER BY pm.DATE ASC
)
SELECT *
FROM rr_cte
It gives me an error, not sure what is wrong in it.
Error
^found "RR_CTE" (at char 145) expecting `SELECT' or `'(''
Any help is appreciated.
Thanks in advance!
You want something like this:
WITH rn AS (
SELECT ROW_NUMBER() OVER (PARTITION BY id ORDER BY DATE ASC) AS RN, t.*
FROM TABLE t
WHERE ID = 1
),
cte AS (
SELECT rn.rn, rn.id, rn.date, return,
100 * (1 + rn.return) as rollingreturn
FROM rn
UNION ALL
SELECT rn.rn, rn.id, rn.date, rn.return,
cte.rollingreturn * (1 + rn.return)
FROM cte JOIN
rn
ON cte.id = rn.id AND rn.rn = cte.rn + 1
)
SELECT *
FROM cte;

Select data from Sybase database but only select the row with the highest sequence

I'm trying to select data from my database from the highest sequence number, I have been struggling with this for a while and cant get it to work.
The database has a lot of Columns with data. I only want data from the row with the highest sequence number to search in, because the data from lower sequences is not of any value for me. Unfortunately the rows from the lower sequences can not be deleted.
Database looks like this:
-----------------------------
| ID | SEQ | rest of the data
-----------------------------
| 1 | 1 | ..
| 1 | 2 | ....
| 2 | 1 | ..
| 1 | 3 | ....
| 3 | 1 | ..
| 1 | 2 | ....
| 4 | 1 | ........
My question is, how can i select only the ID's with the highest sequence number and search in those rows with the WHERE clause?
On oracle11g you can use:
SELECT *
FROM (
SELECT YOUR_TABLE.*, RANK() OVER (PARTITION BY ID oRDER BY SEQ DESC) RN
FROM YOUR_TABLE) A
WHERE RN=1;
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY SEQ DESC ) AS rn
FROM your_table t
)
WHERE rn = 1
or
SELECT ID,
MAX( seq ) AS seq,
MAX( other_column_1 ) KEEP ( DENSE_RANK LAST ORDER BY seq ) AS other_column_1,
MAX( other_column_2 ) KEEP ( DENSE_RANK LAST ORDER BY seq ) AS other_column_2
-- ...
FROM your_table
GROUP BY id
or
SELECT *
FROM your_table t
WHERE seq IN ( SELECT MAX( seq )
FROM your_table x
WHERE x.id = t.id )
or
SELECT t.*
FROM your_table t
INNER JOIN ( SELECT id, MAX( seq ) AS seq
FROM your_table
GROUP BY id ) x
ON ( x.id = t.id AND x.seq = t.seq )

select top N records for each entity

I have a table like below -
ID | Reported Date | Device_ID
-------------------------------------------
1 | 2016-03-09 09:08:32.827 | 1
2 | 2016-03-08 09:08:32.827 | 1
3 | 2016-03-08 09:08:32.827 | 1
4 | 2016-03-10 09:08:32.827 | 2
5 | 2016-03-05 09:08:32.827 | 2
Now, i want a top 1 row based on date column for each device_ID
Expected Output
ID | Reported Date | Device_ID
-------------------------------------------
1 | 2016-03-09 09:08:32.827 | 1
4 | 2016-03-10 09:08:32.827 | 2
I am using SQL Server 2008 R2. i can go and write Stored Procedure to handle it but wanted do it with simple query.
****************EDIT**************************
Answer by 'Felix Pamittan' worked well but for 'N' just change it to
SELECT
Id, [Reported Date], Device_ID
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY Device_ID ORDER BY [ReportedDate] DESC)
FROM tbl
)t
WHERE Rn >= N
He had mentioned this in comment thought to add it to questions so that no body miss it.
Use ROW_NUMBER:
SELECT
Id, [Reported Date], Device_ID
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY Device_ID ORDER BY [ReportedDate] DESC)
FROM tbl
)t
WHERE Rn = 1
You can also try using CTE
With DeviceCTE AS
(SELECT *, ROW_NUMBER() OVER(PARTITION BY Device_ID ORDER BY [Reported Date] DESC) AS Num
FROM tblname)
SELECT Id, [Reported Date], Device_ID
From DeviceCTE
Where Num = 1
If you can't use an analytic function, e.g. because your application layer won't allow it, then you can try the following solution which uses a subquery to arrive at the answer:
SELECT t1.ID, t2.maxDate, t1.Device_ID
INNER JOIN
(
SELECT Device_ID, MAX([Reported Date]) AS maxDate
FROM yourTable
GROUP BY Device_ID
) t2
ON t1.Device_ID = t2.Device_ID AND t1.[Reported Date] = t2.maxDate
Select * from DEVICE_TABLE D
where [Reported Date] = (Select Max([Reported Date]) from DEVICE_TABLE where Device_ID = D.Device_ID)
should do the trick, assume that "top 1 row based on date column" means that you want to select the latest reported date of each Device_ID ?
As for your title, select top 5 rows of each Device_ID
Select * from DEVICE_TABLE D
where [Reported Date] in (Select top 5 [Reported Date] from DEVICE_TABLE D where Device_ID = D.Device_ID)
order by Device_ID, [Reported Date] desc
will give you the top 5 latest reports of each device id.
You may want to sort out the top 5 date if your data isn't in order...
Again with no analytic functions you can use CROSS APPLY :
DECLARE #tbl TABLE(Id INT,[Reported Date] DateTime , Device_ID INT)
INSERT INTO #tbl
VALUES
(1,'2016-03-09 09:08:32.827',1),
(2,'2016-03-08 09:08:32.827',1),
(3,'2016-03-08 09:08:32.827',1),
(4,'2016-03-10 09:08:32.827',2),
(5,'2016-03-05 09:08:32.827',2)
SELECT r.*
FROM ( SELECT DISTINCT Device_ID FROM #tbl ) d
CROSS APPLY ( SELECT TOP 1 *
FROM #tbl t
WHERE d.Device_ID = t.Device_ID ) r
Can be easily modified to support N records.
Credits go to wBob answering this question here

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

T-sql rank for max and min value

I need help with a t-sql query.
I have a table with this structure:
id | OverallRank | FirstRank | SecondRank | Nrank..
1 | 10 | 20 | 30 | 5
2 | 15 | 24 | 12 | 80
3 | 10 | 40 | 37 | 12
I need a query that produces this kind of result:
When id: 1
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 10 | SecondRank | 30 | Nrank | 5
When id: 2
id | OverallRank | BestRankLabel | BestRankValue | WorstRankLabel | WorkRankValue
1 | 15 | FirstRank | 24 | SecondRank | 12
How can I do it?
Thanks in advance
with cte(id, RankValue,RankName) as (
SELECT id, RankValue,RankName
FROM
(SELECT id, OverallRank, FirstRank, SecondRank, Nrank
FROM ##input) p
UNPIVOT
(RankValue FOR RankName IN
(OverallRank, FirstRank, SecondRank, Nrank)
)AS unpvt)
select t1.id, max(case when RankName = 'OverallRank' then RankValue else null end) as OverallRank,
max(case when t1.RankValue = t2.MaxRankValue then RankName else null end) as BestRankName,
MAX(t2.MaxRankValue) as BestRankValue,
max(case when t1.RankValue = t3.MinRankValue then RankName else null end) as WorstRankName,
MAX(t3.MinRankValue) as WorstRankValue
from cte as t1
left join (select id, MAX(RankValue) as MaxRankValue from cte group by id) as t2 on t1.id = t2.id
left join (select id, min(RankValue) as MinRankValue from cte group by id) as t3 on t1.id = t3.id
group by t1.id
Working good with your test data. You should only edit RankName IN (OverallRank, FirstRank, SecondRank, Nrank) by adding right columns' names.
CASE
WHEN OverallRank > FirstRank and OverallRank > FirstSecondRand and OverallRank > nRank THEN 'OverallRank'
WHEN FirstRank > OverallRank ... THEN 'FirstRank'
END
This kind of query is why you should normalise your data.
declare #id int, #numranks int
select #id = 1, #numranks = 3 -- number of Rank columns
;with cte as
(
select *
from
(
select *,
ROW_NUMBER() over (partition by id order by rank desc) rn
from
(
select * from YourBadlyDesignedTable
unpivot (Rank for RankNo in (FirstRank, SecondRank, ThirdRank))u -- etc
) v2
) v1
where id=#id and rn in (1, #numranks)
)
select
tMin.id,
tMin.OverallRank,
tMin.RankNo as BestRankLabel,
tMin.Rank as BestRankValue,
tMax.RankNo as WorstRankLabel,
tMax.Rank as WorstRankValue
from (select * from cte where rn=1) tMin
inner join (select * from cte where rn>1) tMax
on tMin.id = tmax.id
You can take out the id = #id if you want all rows.