How to limit rows on a window function - sql

I want to get a result on one query.
with rws as (
select o.*, first_value(o.ACCT_ID) over(PARTITION by acct_id ORDER by acct_id asc) rn
from ACCT_PLAN o
)
select * from rws
where rn >= 10
and PLAN_STATUS_CD = 'ACTIVE'
order by acct_id;
So I need do get a following result. All rows are "grouped" by partition clause in the window function by acct_id in the CTE. In the main select I need to limit each ACCT_ID if they have more than 10 rows.

You need to COUNT rather then find the FIRST_VALUE if you want to return partitions with 10 or more rows:
WITH rws AS (
SELECT o.*,
COUNT(*) OVER (PARTITION by acct_id ORDER by acct_id ASC) AS cnt
FROM ACCT_PLAN o
)
SELECT *
FROM rws
WHERE cnt >= 10
AND PLAN_STATUS_CD = 'ACTIVE'
ORDER BY acct_id;
If, instead, you want to limit a partition to the first 10 rows, and then to those who are active, then use ROW_NUMBER:
WITH rws AS (
SELECT o.*,
ROW_NUMBER() OVER (PARTITION by acct_id ORDER by acct_id ASC) AS cnt
FROM ACCT_PLAN o
)
SELECT *
FROM rws
WHERE rn <= 10
AND PLAN_STATUS_CD = 'ACTIVE'
ORDER BY acct_id;
Or if you want the first 10 active rows:
WITH rws AS (
SELECT o.*,
ROW_NUMBER() OVER (PARTITION by acct_id ORDER by acct_id ASC) AS cnt
FROM ACCT_PLAN o
WHERE PLAN_STATUS_CD = 'ACTIVE'
)
SELECT *
FROM rws
WHERE rn <= 10
ORDER BY acct_id;

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

SQL select row with max value or distinct value and sum all

I have the following data that is returned to me. I need to get a distinct or max sum of all the commission by taxid for a single repnbr. The 'qtrlycommrep' column is the value I'm trying to get to, but not able to. For repnbr c590, I need to get the 854.66 commission amount, which is the max for each taxid.
What am I doing wrong?
Any help would be much appreciated!
Here's what I've tried so far. Using the Row_number
select distinct
sub.Repnbr
, (sub.QtrLYComm) as qtrlycommrep
from (
select distinct repnbr, QtrLYComm
, rn = row_number() over(partition by repnbr order by QtrLYComm desc)
from #qtrly
) sub
where sub.rn = 1
Cross Apply
select distinct
#qtrly.repnbr
, x.QtrLYComm as qtrlycommrep
from #qtrly
cross apply (
select top 1
*
from #qtrly as i
where i.repnbr = Repnbr
order by i.qtrlycomm desc
) as x;
inner join
select
#qtrly.repnbr, #qtrly.qtrlycomm as qtrlycommrep
from #qtrly
inner join (
select maxvalue = max(qtrlycomm), repnbr
from #qtrly
group by repnbr
) as m
on #qtrly.repnbr = m.repnbr
and #qtrly.qtrlycomm = m.maxvalue;
order by row_number
select top 1 with ties
#qtrly.repnbr, #qtrly.qtrlycomm as qtrlycommrep
from #qtrly
order by
row_number() over(partition by repnbr
order by qtrlycomm desc)
You want one value per tax id. You need to include that. For instance:
select q.Repnbr, sum(q.QtrLYComm) as qtrlycommrep
from (select q.*,
row_number() over(partition by repnbr, taxid order by QtrLYComm desc) as seqnum
from #qtrly q
) q
where seqnum = 1
group by q.Repnbr;
However, I would be inclined to use two levels of aggregation:
select q.Repnbr, sum(q.QtrLYComm) as qtrlycommrep
from (select distinct repnbr, taxid, QtrLYComm
from #qtrly q
) q
group by q.Repnbr;

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

Sql query to fetch second latest entry from a table

I have a table with below mentioned columns. I want to fetch the previous status of customer. Once customer id can have multiple entries
Customer_id status start_date end_date Active
1 Member 01-JAN-18 04-FEB-18 N
1 Explorist 05-FEB-18 30-APR-18 N
1 Globalist 01-MAY-18 31-DEC-99 Y
Desired output
Customer _id Previous_status end_date
1 Explorist 30-APR-18
Please try below query using QUALIFY keyword and ROW_NUMBER():
SELECT a.* from table a
QUALIFY ROW_NUMBER OVER(PARTITION BY customer_id order by start_date desc) = 2
Below query should work.
SELECT * from (
SELECT a.*,
ROW_NUMBER() over (partition by customer_id order by start_date desc) rn
from table a )
where rn =2
You can use below query and I guess that is very simple and that worked for me,
select * from customer order by end_date desc limit 1,1
Consider this question: Select Nth Row From A Table In Oracle
In your case, that would be:
select * from (select a.*, rownum rnum from (select * from <your table name>
order by <start_date or end_date> desc) a where rownum <= 2) where rnum >= 2;
If you are using Oracle DataBase then try below query using ROW_NUMBER() function:Let's consider the table name is customer
SELECT TEMP.CUSTOMER_ID
,TEMP.STATUS
,TEMP.START_DATE
,TEMP.END_DATE
,TEMP.ACTIVE
FROM(
SELECT ROW_NUMBER() OVER (PARTITION BY CUSTOMER_ID ORDER BY CUSTOMER_ID ASC,START_DATE DESC) AS "ROW_NUM"
,CUSTOMER_ID
,STATUS
,START_DATE
,END_DATE
,ACTIVE
FROM CUSTOMER) TEMP
WHERE TEMP."ROW_NUM" = 2;

If Rownumber = 1 and Condition Then Condition

I've got a question about a sql result and how to achieve the following.
In the Screenshot, there is a Rownumber for every ID, and every ID has another column which has a status 'old' or 'processed'. What i want is, if the RN is = 1 and the Status is processed, than all other RN of this ID should also have the status 'processed'.
Is there a possibility to achieve this in sql?
SELECT RN = ROW_NUMBER() OVER (PARTITION BY [NODE_NAME]
ORDER by REPORTING_RELEVANT_STATUS_ID DESC, BILLING_PERIOD DESC)
,[CI_EQUIPMENT_ID] AS ID_PART
,[REPORTING_RELEVANT_STATUS_ID] AS REPORTING_RELEVANT
,[BILLING_PERIOD]
, [NODE_NAME]
FROM Table
Put your query in CTE? then JOIN it with actual table:
;WITH cte AS (
SELECT RN = ROW_NUMBER() OVER (PARTITION BY [NODE_NAME]
ORDER by REPORTING_RELEVANT_STATUS_ID DESC, BILLING_PERIOD DESC)
,[CI_EQUIPMENT_ID] AS ID_PART
,[REPORTING_RELEVANT_STATUS_ID] AS REPORTING_RELEVANT
,[BILLING_PERIOD]
, [NODE_NAME]
FROM Table
)
SELECT t.[CI_EQUIPMENT_ID] AS ID_PART,
CASE WHEN c.RN is NOT NULL THEN c.REPORTING_RELEVANT ELSE t.[REPORTING_RELEVANT_STATUS_ID] END AS REPORTING_RELEVANT,
t.[BILLING_PERIOD],
t.[NODE_NAME]
FROM Table t
LEFT JOIN (
SELECT *
FROM cte
WHERE RN = 1 AND REPORTING_RELEVANT = 'PROCESSED'
) as c
ON c.ID_PART = t.[CI_EQUIPMENT_ID]
Creating common table expression
Updating the CTE values based on RN=1 and Status=Processed. using the Self Join
here:
;with CTE (RN,ID_PART,REPORTING_RELEVANT,BILLING_PERIOD,NODE_NAME) AS
(
SELECT RN = ROW_NUMBER() OVER (PARTITION BY [NODE_NAME]
ORDER by REPORTING_RELEVANT_STATUS_ID DESC, BILLING_PERIOD DESC)
,[CI_EQUIPMENT_ID] AS ID_PART
,[REPORTING_RELEVANT_STATUS_ID] AS REPORTING_RELEVANT
,[BILLING_PERIOD]
, [NODE_NAME]
FROM Table
)
Update CTE1
set CTE1.Status='Processed'
from CTE CTE1 inner join CTE CTE2
on CTE1.ID_PART=CTE2.ID_PART
where CTE2.RN=1 and CTE2.Status='Processed'