T-SQL - Get last 30 Rows for eacht ID - sql

how can i get the last 30 rows in a month for each employee? i have a table with evaluations for each employee.
SELECT
Date,
Month,
Team,
Employee_ID,
Evaluation_Score,
Evaluation_Case_Number
From X
Where month = #month
Order by date desc
This is what i got, but i only want to see the last 30 Evaluation Scores (or less, if they don't have that many) for the declared month.
Is there a way to do this? Thanks in Advance.

You can use row_number(). Something like this:
select x.*
from (select x.*,
row_number() over (partition by employee_id order by date desc) as seqnum
from x
where month = #month
) x
where seqnum <= 30;

Use the TOP statement:
SELECT TOP(30)
Date,
Month,
Team,
Employee_ID,
Evaluation_Score,
Evaluation_Case_Number
From X
Where month = #month
Order by date desc
This will help you to limit the number of returned rows.

Related

Compare current month sales with previous month sales

Code to get the test data:
create table SalesCalls
(
EmpId INT NOT NULL,
EmpName nvarchar(20),
month INT,
Year INT,
CallsMade INT
)
GO
Insert into SalesCalls values
(1,'ABC',12,2018,10),
(1,'ABC',1,2019,15),
(1,'ABC',2,2019,20),
(2,'DEF',12,2018,12),
(2,'DEF',1,2019,14),
(2,'DEF',2,2019,26)
GO
The objective is to compare the current month sales of an Employee with the previous month sales of that Employee and find out the percentage change in it. Achieved that using the below query:
With SalesCTE as
(
select EmpId,EmpName,
Month As CurrentMonth,
Year as CurrentMonthYear,
Case When month = 1 then 12 Else (Month-1) End AS PrevMonth,
Case when month = 1 then (Year - 1) Else Year End As PrevMonthYear,
CallsMade
from SalesCalls
)
select
S1.EmpId, S1.EmpName, S1.CurrentMonth, S1.CurrentMonthYear, S1.CallsMade as CurrentMonthCalls,
S2.CurrentMonth as PrevMont,
S2.CurrentMonthYear as PrevMonthYear,
S2.CallsMade as PrevMonthCalls,
( CONVERT(numeric(5,2),S1.CallsMade) / S2.CallsMade) * 100 As PercentageChange
from SalesCTE S1
JOIN SalesCTE S2 ON S1.EmpId = S2.EmpId
AND S1.PrevMonth = S2.CurrentMonth
AND S1.PrevMonthYear = S2.CurrentMonthYear
ORDER BY S1.EmpId, S1.CurrentMonth, S1.CurrentMonthYear
The above query worked until the time there are no redundant records for an Employee for the same month.
But later data from multiple sources is coming in and an Employee table can have multiple records for the same month and it is still valid. Because the employee could be making calls in different ways. An as example the below record is inserted into the table:
Insert into SalesCalls values
(1,'ABC',1,2019,1)
Now the above query which worked fine above for the comparison of current month SalesCalls with the previous month is no longer working.
Phase 2 of the use case:
So to fix this I have build an intermediate temp table that contains aggregate data. The query used is:
Select EmpId, EmpName, month, Year, SUM(CallsMade) as CallsMade
into #SalesCalls
from SalesCalls
group by EmpId, EmpName, month, Year
Now the SalesCalls table inside the CTE is replaced with #SalesCalls and then the above query works fine.
But this #SalesCalls table needs to be dropped and recreated every time to see the latest comparison data.
The question is, is it possible to get the comparison data using a single query only and no intermediate temp tables or views.
Just use window functions:
select EmpId, EmpName, month, Year,
sum(CallsMade) as CallsMade,
(case when lag(year * 12 + month) over (partition by empId order by year, month) = year * 12 + month - 1
then lag(sum(callsMade)) over (partition by empId order by year, month)
end) as prevMonthCalls,
(case when lag(year * 12 + month) over (partition by empId order by year, month) = year * 12 + month - 1
then callsMade * 100.0 / lag(sum(callsMade) over (partition by empId order by year, month)
end) as as perentageChange
from SalesCalls
group by EmpId, EmpName, month, Year;
No joins, CTEs, subqueries, or temporary tables are needed at all.
One of the simplest solutions :
select EmpId, EmpName, month, Year,
sum(CallsMade) as CallsMade,
lag(sum(callsMade)) over (partition by empId order by year, month) AS prevMonthCalls,
sum(CallsMade) * 100.0 / lag(sum(CallsMade)) over (partition by empId order by year, month) as PercentageChange
from SalesCalls
group by EmpId, EmpName, month, Year
order by EmpId, Year,month;

SQL order with equal group size

I have a table with columns month, name and transaction_id. I would like to count the number of transactions per month and name. However, for each month I want to have the top N names with the highest transaction counts.
The following query groups by month and name. However the LIMIT is applied to the complete result and not per month:
SELECT
month,
name,
COUNT(*) AS transaction_count
FROM my_table
GROUP BY month, name
ORDER BY month, transaction_count DESC
LIMIT N
Does anyone have an idea how I can get the top N results per month?
Use row_number():
SELECT month, name, transaction_count
FROM (SELECT month, name, COUNT(*) AS transaction_count,
ROW_NUMBER() OVER (PARTITION BY month ORDER BY COUNT(*) DESC) as seqnum
FROM my_table
GROUP BY month, name
) mn
WHERE seqnum <= N
ORDER BY month, transaction_count DESC

Grouping Start Dates

Example of what I am trying to do:
I have 10 employees. They all started on different days throughout the year. Each get paid once a week. I want to query their first paycheck and call that week 1 for all employees. Then each subsequent paycheck will be 2...3...through 13. So basically I want to see what each of their first 13 weeks on the job looked like stacked against each other. I would expect my output to look something like this:
You can use row_number():
select
row_number() over(partition by EmployeeId order by PaycheckDate) week,
EmployeeId,
PaycheckDate,
Amount
from mytable
order by week, EmployeeId
If you want just the first 13 weeks per employee, then:
select *
from (
select
row_number() over(partition by EmployeeId order by PaycheckDate) week,
EmployeeId,
PaycheckDate,
Amount
from mytable
) t
where week <= 13
order by week, EmployeeId

How do I get all rows from the second to latest date?

I have gotten all rows for the latest date like this:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date = (SELECT MAX(date) FROM regions)
ORDER BY 1
So how would I get the rows for the second latest date?
I have tried but no luck:
SELECT MAX(date), quarter, sales_region, revenue
FROM regions
WHERE date < (SELECT MAX(date) FROM regions)
ORDER BY 1
Here is one method:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date = (SELECT DISTINCT date
FROM regions r2
ORDER BY date DESC
OFFSET 1 FETCH FIRST 1 ROW ONLY
)
ORDER BY 1;
Another method uses dense_rank():
select r.*
from (select r.*, dense_rank() over (order by date desc) as seqnum
from regions r
) r
where seqnum = 2;
Gordon answered your question precisely, but if you want to get the records for the last two dates in one query, you could use IN instead of =, and get the top two records with LIMIT 2:
SELECT date, quarter, sales_region, revenue
FROM regions
WHERE date IN (SELECT DISTINCT date
FROM regions r2
ORDER BY date DESC
LIMIT 2)
ORDER BY 1;
Starting with version 8.4, you can also use FETCH FIRST 2 ROW ONLY instead of LIMIT 2.

Last day of the month with a twist in SQLPLUS

I would appreciate a little expert help please.
in an SQL SELECT statement I am trying to get the last day with data per month for the last year.
Example, I am easily able to get the last day of each month and join that to my data table, but the problem is, if the last day of the month does not have data, then there is no returned data. What I need is for the SELECT to return the last day with data for the month.
This is probably easy to do, but to be honest, my brain fart is starting to hurt.
I've attached the select below that works for returning the data for only the last day of the month for the last 12 months.
Thanks in advance for your help!
SELECT fd.cust_id,fd.server_name,fd.instance_name,
TRUNC(fd.coll_date) AS coll_date,fd.column_name
FROM super_table fd,
(SELECT TRUNC(daterange,'MM')-1 first_of_month
FROM (
select TRUNC(sysdate-365,'MM') + level as DateRange
from dual
connect by level<=365)
GROUP BY TRUNC(daterange,'MM')) fom
WHERE fd.cust_id = :CUST_ID
AND fd.coll_date > SYSDATE-400
AND TRUNC(fd.coll_date) = fom.first_of_month
GROUP BY fd.cust_id,fd.server_name,fd.instance_name,
TRUNC(fd.coll_date),fd.column_name
ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)
You probably need to group your data so that each month's data is in the group, and then within the group select the maximum date present. The sub-query might be:
SELECT MAX(coll_date) AS last_day_of_month
FROM Super_Table AS fd
GROUP BY YEAR(coll_date) * 100 + MONTH(coll_date);
This presumes that the functions YEAR() and MONTH() exist to extract the year and month from a date as an integer value. Clearly, this doesn't constrain the range of dates - you can do that, too. If you don't have the functions in Oracle, then you do some sort of manipulation to get the equivalent result.
Using information from Rhose (thanks):
SELECT MAX(coll_date) AS last_day_of_month
FROM Super_Table AS fd
GROUP BY TO_CHAR(coll_date, 'YYYYMM');
This achieves the same net result, putting all dates from the same calendar month into a group and then determining the maximum value present within that group.
Here's another approach, if ANSI row_number() is supported:
with RevDayRanked(itemDate,rn) as (
select
cast(coll_date as date),
row_number() over (
partition by datediff(month,coll_date,'2000-01-01') -- rewrite datediff as needed for your platform
order by coll_date desc
)
from super_table
)
select itemDate
from RevDayRanked
where rn = 1;
Rows numbered 1 will be nondeterministically chosen among rows on the last active date of the month, so you don't need distinct. If you want information out of the table for all rows on these dates, use rank() over days instead of row_number() over coll_date values, so a value of 1 appears for any row on the last active date of the month, and select the additional columns you need:
with RevDayRanked(cust_id, server_name, coll_date, rk) as (
select
cust_id, server_name, coll_date,
rank() over (
partition by datediff(month,coll_date,'2000-01-01')
order by cast(coll_date as date) desc
)
from super_table
)
select cust_id, server_name, coll_date
from RevDayRanked
where rk = 1;
If row_number() and rank() aren't supported, another approach is this (for the second query above). Select all rows from your table for which there's no row in the table from a later day in the same month.
select
cust_id, server_name, coll_date
from super_table as ST1
where not exists (
select *
from super_table as ST2
where datediff(month,ST1.coll_date,ST2.coll_date) = 0
and cast(ST2.coll_date as date) > cast(ST1.coll_date as date)
)
If you have to do this kind of thing a lot, see if you can create an index over computed columns that hold cast(coll_date as date) and a month indicator like datediff(month,'2001-01-01',coll_date). That'll make more of the predicates SARGs.
Putting the above pieces together, would something like this work for you?
SELECT fd.cust_id,
fd.server_name,
fd.instance_name,
TRUNC(fd.coll_date) AS coll_date,
fd.column_name
FROM super_table fd,
WHERE fd.cust_id = :CUST_ID
AND TRUNC(fd.coll_date) IN (
SELECT MAX(TRUNC(coll_date))
FROM super_table
WHERE coll_date > SYSDATE - 400
AND cust_id = :CUST_ID
GROUP BY TO_CHAR(coll_date,'YYYYMM')
)
GROUP BY fd.cust_id,fd.server_name,fd.instance_name,TRUNC(fd.coll_date),fd.column_name
ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)