Select max and min values at the same SQL query - sql

I have a SQL table with the columns: ID, ADate, XValue and Type. For each "Type" value, I'd like to retrieve the most recent row (per ADate), the least recent row (per ADate) and also the row containing the highest "XValue" value.
Is it possible to be performed through a single SQL query? I've tried a few times with no success.

Using row_number() you could do something like this:
select ID, ADate, XValue and Type
from (
select *
, min_adate = row_number() over (partition by type order by adate asc)
, max_adate = row_number() over (partition by type order by adate desc)
, max_xvalue = row_number() over (partition by type order by xvalue desc)
from t
) as sub
where 1 in (min_adate,max_adate,max_xvalue)
/* --also could be written as
where min_adate = 1
or max_adate = 1
or max_xvalue = 1
*/
Using a common table expression may make it more readable.
;with cte as (
select *
, min_adate = row_number() over (partition by type order by adate asc)
, max_adate = row_number() over (partition by type order by adate desc)
, max_xvalue = row_number() over (partition by type order by xvalue desc)
from t
)
select ID, ADate, XValue and Type
from cte
where 1 in (min_adate,max_adate,max_xvalue)

select ID, ADate, XValue,[Type]
from (
select *
,row_number() over (partition by type order by adate asc) AS DateSeq
,row_number() over (partition by type) AS TypeCount
,row_number() over (partition by type order by xvalue desc) AS ValueSeq
from t
) as tt
where tt.DateSeq=1 OR tt.DateSeq=tt.TypeCount OR tt.ValueSeq=1

One method uses row_number():
select t.*
from (select t.*,
row_number() over (partition by type order by aDate desc) as seqnum_adate_desc,
row_number() over (partition by type order by aDate asc) as seqnum_adate_asc,
row_number() over (partition by type order by XValue desc) as seqnum_xvalue_desc
from t
) t
where 1 in (seqnum_adate_desc, seqnum_adate_asc, seqnum_xvalue_desc);

Related

Get last and first record using rank()

I need to get first and last record (ordered by Date column) from table for certain SSID. It is not a problem if there is more records with same max or min date. All I need is union all.
I am getting last record having max(date) with:
with c as (
select *, rnk = rank() over (partition by Date order by Date ASC)
from table
where SSID = '00921834800'
)
select top 1 Date, City, Title
from c
order by Date desc
How to I get first record (min(Date)) as well (same thing only with order by Date asc) with single select and without using ranking again?
I'm using MSSQL 2017.
; with c as (
select *,
rnk = rank() over (partition by Date order by Date ASC),
rnk2 = rank() over (partition by Date order by Date desc)
from table
where SSID= '00921834800'
)
select Date,
City,
Title
from c
where rnk = 1 or rnk2 = 1
order by Date desc
I would use the following query:
select * from (select top 1 with ties * from t where ssid = '00921834800' order by date) as a
union all
select * from (select top 1 with ties * from t where ssid = '00921834800' order by date desc) as b
One other solution is :
with
c as
(
select *,
rank() over (partition by Date order by Date ASC) AS RNK,
count() OVER (partition by Date) AS CNT
from table
where SSID= '00921834800')
select Date, City, Title
from c
WHERE RNK = 1
OR CNT = RNK
order by Date desc

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

how to update all rows except first?

I have this sql query :
update CCUSTOMERINFO set VALIDTO=sysdate where (
select * from (
select row_number() over (order by created desc) rn, customer_id, CCUSTOMERINFO.VALIDTO
from CCUSTOMERINFO
where customer_id=100309772 order by created DESC) where rn > 1);
But it say it have some mistake.
This query returns all i want to update :
select * from (
select row_number() over (order by created desc) rn, customer_id, CCUSTOMERINFO.VALIDTO
from CCUSTOMERINFO
where customer_id=100309772 order by created DESC) where rn > 1)
Any suggestion how can i do that?
Use it like the below
update CCUSTOMERINFO set VALIDTO=sysdate where rowid in (
select row_id from (
select row_number() over (order by created desc) rn, customer_id, rowid row_id,
CCUSTOMERINFO.VALIDTO
from CCUSTOMERINFO
where customer_id=100309772 order by created DESC) where rn > 1);

Max and Min row numbers from single cte code block

;with cte1 as
( select id,
row_number() over (partition by pk,pp,sn order by id asc)
as rn
from mqms_production
) select * into #M from cte1 where rn=1
With the above, I get all the rows with rn=1 but I also want to copy to another table all the rows with max rn for a given partition pk,pp, sn.
Is it possibleto do it without having to write the cte block again with
(partition by pk,pp,sn order by id DESC)
thanks!
You can just add another window function based expression there with reverse sort order and get the top rows on both of them
with cte1 as (
select id,
row_number() over (partition by pk,pp,sn order by id asc) as rn1,
row_number() over (partition by pk,pp,sn order by id desc) as rn2
from mqms_production
)
select * from cte1
where rn1 = 1 or rn2 = 1;

Get max value of column, min value of column corresponding to rank at once in ms sql

I have this query
WITH summary AS
(
SELECT Msisdn, DateRegistered ,
RANK() OVER (ORDER BY DateRegistered ASC) AS dRank
FROM dbo.SubscriptionsArchive
WHERE MSISDN='123456'
)
SELECT s.msisdn, s.DateRegistered AS firstReg
FROM summary s
WHERE dRank =(SELECT max(dRank) FROM summary )
This displays the firstReg corresponding to the min Rank, I want to get the lastReg corresponding to maxRank at the same time.
How do I achieve this?
Solution by cross joining the first and last line of the CTE, with TOP 1 syntax:
WITH summary AS
(
SELECT Msisdn, DateRegistered ,
RANK() OVER (ORDER BY DateRegistered ASC) AS dRank
FROM dbo.SubscriptionsArchive
WHERE MSISDN='123456'
)
SELECT minrow.*, maxrow.*
FROM
(select TOP 1 * from summary order by dRank desc) minrow
CROSS JOIN
(select TOP 1 * from summary order by dRank asc) maxrow ;
WITH summary AS
(
SELECT Msisdn, DateRegistered ,
RANK() OVER (ORDER BY DateRegistered ASC) AS ASCRank,
RANK() OVER (ORDER BY DateRegistered DESC) AS DESCRank
FROM dbo.SubscriptionsArchive
WHERE MSISDN='123456'
)
SELECT s.msisdn,
(CASE WHEN ASCRAnk=1 THEN s.DateRegistered END) AS firstReg,
(CASE WHEN DESCRAnk=1 THEN s.DateRegistered END) AS LASTReg
FROM summary s