Use min() when row_number() is descending - sql-server

I have #Tickets table with 2 open tickets for JFK.
declare #Tickets table
(
Airport varchar(10),
TicketNum varchar(10),
Created date,
Modified date,
LastModified date,
Modified_By varchar(10),
TicketStatus varchar(10),
AssignedTo varchar(10)
)
insert into #Tickets
select 'JFK', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'JFK', '002', '9/28/2021', '9/28/2021', '9/30/2021', 'Mary', 'Open', 'Andrew'
select Airport, lastmodified, assignedto, Modified_By
from
(
select airport, lastmodified, assignedto, Modified_By,
row_number() over(partition by airport order by lastmodified desc) rn
from #Tickets
) src
where rn = 1
The following returns the last modified date of JFK tickets (9/30/2021), the last person that modified any of the JFK tickets (Mary) and the owner of the ticket that Mary last modified (Andrew).
Airport lastmodified assignedto Modified_By
JFK 2021-09-30 Andrew Mary
What I can't figure out is how to show min(created) to show when the first ticket was created.
The complete result should be
Airport First_Created lastmodified assignedto Modified_By
JFK 2021-09-25 2021-09-30 Andrew Mary
How can I plug in a min(create_date) as 'First_Created' in the query above?
I'm sure I can have two cte like below and then a join, but I prefer not using joins unless there are no other options:
declare #Tickets table
(
Airport varchar(10),
TicketNum varchar(10),
Created date,
Modified date,
LastModified date,
Modified_By varchar(10),
TicketStatus varchar(10),
AssignedTo varchar(10)
)
insert into #Tickets
select 'JFK', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'EWR', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'STI', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'JFK', '002', '9/28/2021', '9/28/2021', '9/30/2021', 'Mary', 'Open', 'Andrew'
;with cte as
(
select Airport, lastmodified, assignedto, Modified_By
from
(
select airport, lastmodified, assignedto, Modified_By,
row_number() over(partition by airport order by lastmodified desc) rn
from #Tickets
) src
where rn = 1
), cte2 as
(
select airport, min(created) as 'created' from #Tickets group by airport
)
select cte.Airport, lastmodified, assignedto, Modified_By, created
from cte inner join cte2 on
cte.Airport = cte2.Airport

You could try something like this. The query selects the latest modified row (partitioned by Airport) and CROSS APPLY's the earliest Created date.
with last_mod_cte as (
select top 1 with ties *
from #tickets
order by row_number() over(partition by airport order by lastmodified desc))
select lm.Airport, min_created.dt First_Created, lm.LastModified, lm.AssignedTo, lm.Modified_By
from last_mod_cte lm
cross apply (select min(Created)
from #Tickets t
where lm.Airport=t.Airport) min_created(dt);

Related

how can i know the total number of holidays by employee

I have this tables Holiday(Id,FK(EmployeeId),StartDate,EndDate) and table Employee(Id,FullName,etc...)
I want to know the number of days that each employee have
I was trying something like this :
SELECT Employee.Id, SUM(DATEDIFF(day,Holiday.StartDate,Holiday.EndDate) + 1)
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
GROUP BY Employee.id
i know this doesn't work because, to sum thati would need to group by Holiday.Id since i will have many rows in the Holiday table for the same EmployeeId
how can i accomplish this?
thanks for the help
Or, using a workingday calculation I found here: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server you could do the following:
CREATE FUNCTION workingdays ( #DateFrom Date, #DateTo Date) RETURNS INT AS
BEGIN
DECLARE #TotDays INT= DATEDIFF(DAY, #DateFrom, #DateTo) + 1;
DECLARE #TotWeeks INT= DATEDIFF(WEEK, #DateFrom, #DateTo) * 2;
DECLARE #IsSunday INT= CASE
WHEN DATENAME(WEEKDAY, #DateFrom) = 'Sunday'
THEN 1
ELSE 0
END;
DECLARE #IsSaturday INT= CASE
WHEN DATENAME(WEEKDAY, #DateTo) = 'Saturday'
THEN 1
ELSE 0
END;
DECLARE #TotWorkingDays INT= #TotDays - #TotWeeks - #IsSunday + #IsSaturday;
RETURN #TotWorkingDays;
END
GO
create table Employee (Id int identity, name varchar(64), Primary Key (Id));
create table Holiday (EmployeeId int, StartDate date, EndDate date);
insert into Employee VALUES ('Harry Potter'),('Hermiony Granger'),('Ron Weasly'),('Ginny Weasley');
insert into Holiday VALUES (1,'2020-02-12','2020-02-18'),(1,'2020-04-02','2020-04-07'),(1,'2020-08-21','2020-09-05'),
(2,'2020-01-04','2020-01-13'),(2,'2020-03-17','2020-03-23'),(2,'2020-05-29','2020-06-7');
SELECT Employee.Id, SUM(dbo.workingdays(Holiday.StartDate,Holiday.EndDate))
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
GROUP BY Employee.id
This will still only be a crude estimation as is does not account for public holidays.
DEMO: https://rextester.com/QKGUT41272
Hi i think you can build query using CTE like this :
Resource : CTE Microsoft : https://learn.microsoft.com/fr-fr/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-ver15
Or using select in clause form
EDIT : Or it work with your current query to
create Table #EMPLOYEE
(
EMP_ID INT,
EMP_NAME varchar(128)
)
create Table #HOLIDAYS
(
HL_ID INT,
HL_EMP varchar(128),
StartDate DATE,
EndDate DATE
)
Insert into #EMPLOYEE
(
EMP_ID,
EMP_NAME
)
SELECT 1, 'Toto'
UNION ALL
SELECT 2,'Dupont'
UNION ALL
SELECT 3,'Titi'
UNION ALL
SELECT 4,'Tata'
Insert into #HOLIDAYS
(
HL_ID,
HL_EMP,
StartDate,
EndDate
)
SELECT '1', '1',GETDATE(),DATEADD(day,4,GETDATE())
UNION ALL
SELECT '2','1',DATEADD(day,-7,GETDATE()), DATEADD(day,-5,GETDATE())
UNION ALL
SELECT '3','2',DATEADD(day,4,GETDATE()),DATEADD(day,15,GETDATE())
-- USING CTE EXEMPLE
;WITH MyCteAPP AS (
SELECT EMP_ID, EMP_NAME, HL_ID, ISNULL(StartDate,GETDATE()) AS 'StartDate', ISNULL(EndDate,GETDATE()) AS 'EndDate'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
)
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,StartDate,EndDate)) AS 'NbDay'
FROM MyCteAPP
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
-- USING SELECT IN FROM EXEMPLE
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,StartDate,EndDate)) AS 'NbDay'
FROM (SELECT EMP_ID, EMP_NAME, HL_ID, ISNULL(StartDate,GETDATE()) AS 'StartDate', ISNULL(EndDate,GETDATE()) AS 'EndDate'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
) AS SUBQUERY
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
--USING CURRENT QUERY
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,ISNULL(StartDate,GETDATE()),ISNULL(EndDate,GETDATE()))) AS 'NbDay'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
DROP TABLE #EMPLOYEE
DROP TABLE #HOLIDAYS
RESULT :
I have tried with sub query. Please use below query. It will be helpful.
SELECT Id, SUM(leave) AS leave
FROM
(
SELECT Employee.Id, DATEDIFF(dd,Holiday.StartDate,Holiday.EndDate) as leave
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
)a
GROUP BY ID

How to make one row and sum up values in SQL Server 2012

I need to make one line per PolicyNumber.
First, I need to check if there is more than one ControlNo per PolicyNumber,
if yes, than I need to sum premium per PolicyNumber and leave only the row where StatusReason = "Rewrite (Agency Reason)".
Code example:
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable
(
EffectiveDate DATE,
QuoteID INT,
ControlNo INT,
PolicyNumber VARCHAR(50),
PolicyType VARCHAR(50),
StatusReason VARCHAR(100),
Premium MONEY
)
INSERT INTO #MyTable
VALUES ('2019-01-01', 884867, 1111, 'Poliy1', 'New Business', 'Rewrite (Agency Reason)', 0.00),
('2019-01-01', 993456,2222, 'Poliy1', 'Rewrite', 'Driver Added', 1965.00),
('2019-03-01', 54545,3333, 'Poliy2', 'Rewrite', 'Other Reason', 1123.00),
('2019-04-01', 78935,4444, 'Poliy3', 'Renewal', 'Rewrite (Agency Reason)', 0.00),
('2019-04-01', 88884,5555, 'Poliy3', 'Rewrite', 'Driver Added', 100.00)
SELECT * FROM #MyTable
The output should be like this:
Assuming the ControlNo is sequential, you can use row_number() to identify the top record.
Select EffectiveDate
,PolicyNumber
,PolicyType = max(case when RN=1 then PolicyType end)
,Premium = sum(Premium)
From (Select *
,RN = Row_Number() over (Partition By EffectiveDate,PolicyNumber Order By ControlNo)
From #MyTable
) A
Group By
EffectiveDate
,PolicyNumber
Returns
EffectiveDate PolicyNumber PolicyType Premium
2019-01-01 Poliy1 New Business 1965.00
2019-03-01 Poliy2 Rewrite 1123.00
2019-04-01 Poliy3 Renewal 100.00

Removing Subqueries

I have 2 tables tab1 and tab2.
tab1:
id name monthid salary inflow
-----------------------------------------
1 mohan 1 2000 1000
1 mohan 3 3000 1000
1 mohan 4 4500 1600
1 mohan 2 2500 1200
in tab2 I want this output:
id name salary inflow
--------------------------
1 mohan 12000 1600
In tab2, salary column is the sum of salary of tab1 and inflow is the inflow of highest month.
I tried this query:
Insert into tab2(id, name, salary)
select id, name, sum(salary)
from tab1
update tab2
set inflow = (select inflow
from tab1
where monthid = max(monthid))
But I know this is not the correct method.
Can anyone help me to correct this query? And I also want to remove the subqueries.
You can use row_number as below
Insert into tab2(id, [name], [salary], inflow)
Select id, [name], Salary, inflow from (
Select id, [name], sum(salary) over(partition by id) as Salary,
inflow, RowN = Row_number() over (partition by id order by monthid desc) from tab1 ) a
Where a.RowN = 1
Without subquery you can use top(1) with ties as below
Insert into tab2(id, [name], [salary], inflow)
Select top (1) with ties id, [name], sum(salary) over(partition by id) as salary, inflow
from tab1
order by Row_number() over (partition by id order by monthid desc)
DECLARE #tab1 table(id int,name varchar(100),monthid int, salary int,inflow int)
INSERT INTO #tab1
SELECT 1,'Mohan',1,2000,1000
UNION ALL
SELECT 1,'Mohan',3,3000,1000
UNION ALL
SELECT 1,'Mohan',4,4500,1600
UNION ALL
SELECT 1,'Mohan',2,2500,1200
SELECT top 1
id, name,SUM(salary) OVER(PARTITION BY id) as salary,MAX(inflow) OVER(PARTITION BY id) as inflow
FROM #tab1
OR
SELECT DISTINCT
id, name,SUM(salary) OVER(PARTITION BY id) as salary,MAX(inflow) OVER(PARTITION BY id) as inflow
FROM #tab1

Count and Row_Number

I want to get the top 5 Zipcodes for each Store with the highest Customers in them (zipcodes).
Please find below my query:
SELECT T.[Store], T.[ZipCode], Count(T.[Customer])
FROM ( SELECT T.[Store], T.[ZipCode],
Count(T.[Customer]) row_number() over (Partition By T.[StoreGitanjali] Order By Count (T.[Customer]) desc) as RN
FROM [Marketing].[dbo].[Poscus] as T
Group By T.[StoreGitanjali], T.[ZipCode]) as T
where T.RN <=5
Group By T.[StoreGitanjali], T.[ZipCode]
Please let me know how to use Count here in this scenario.
Thank you!
Example
CREATE TABLE #t
(
ID INT IDENTITY(1,1),
Customer NVARCHAR(3),
Store NVARCHAR(5),
ZIP INT
)
INSERT INTO #t VALUES('a', 'XYZ', 1234)
,('b', 'XYZ', 1234)
,('c', 'PQR', 1231)
,('d', 'PQR', 1231)
,('e', 'PQR', 1231)
,('f', 'XYZ', 1232)
,('g', 'XYZ', 1232)
,('h', 'XYZ', 1232)
,('i', 'PQR', 1236)
,('j', 'PQR', 1236)
,('k', 'LMN', 1237)
SELECT * FROM #t
The solution is, Set WHERE part < 2 according to your requirement.
SELECT TotalCustomer, Store, ZIP, Part FROM (
SELECT
COUNT(1) AS TotalCustomer,
Store,
ZIP,
ROW_NUMBER() OVER (PARTITION BY Store ORDER BY Store) AS Part
FROM #t
GROUP BY Store, ZIP
) t
WHERE Part < 2
ORDER BY Part
;WITH CTE
AS(
SELECT Store
,Zip
,COUNT(DISTINCT Customer) AS CustCount
FROM #t
GROUP BY Store,Zip
--ORDER BY Store,Zip
)
SELECT A.*
FROM(
SELECT *
--,DENSE_RANK() OVER(PARTITION BY Store ORDER BY CustCount DESC) AS DenRank
,ROW_NUMBER() OVER(PARTITION BY Store ORDER BY CustCount DESC) AS DenRank
FROM CTE
--ORDER BY Store,Zip
) AS A
WHERE A.DenRank <= 2

Repeat the first date withing a group

I Would like the first date of each group to repeat for the rest of the rows withing each group
You could use window expressions and grouping;
FIRST_VALUE (Transact-SQL)
You would need to partition by your first column. to get the split of A and B.
For example;
with cteTempData
(
[Code]
, [Date]
)
as
(
select 'A',cast('2015-9-4' as date)
union all select 'A','2015-9-4'
union all select 'A','2015-9-4'
union all select 'A','2015-9-16'
union all select 'B','2015-9-16'
union all select 'B','2015-9-22'
union all select 'B','2015-9-22'
union all select 'B','2015-10-26'
union all select 'B','2015-10-30'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
from cteTempData
Using the first_value syntax also allows you to work with other columns in that ordered record....
with cteTempData
(
[Code]
, [Date]
, [Comment]
)
as
(
select 'A',cast('2015-9-4' as date),'One'
union all select 'A','2015-9-4','Two'
union all select 'A','2015-9-4','Three'
union all select 'A','2015-9-16','Four'
union all select 'B','2015-9-16','Five'
union all select 'B','2015-9-22','Six'
union all select 'B','2015-9-22','Seven'
union all select 'B','2015-10-26','Eight'
union all select 'B','2015-10-30','Nine'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
, FIRST_VALUE([Comment]) over (partition by [Code] order by [Date]) as [First_Comment]
from cteTempData
Use MIN() Over ()
Declare #Table table (Grp varchar(25),Date date)
Insert into #Table values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
Select *
,GrpDate = min(Date) over (Partition By Grp)
From #Table
Returns
Grp Date GrpDate
A 2015-09-04 2015-09-04
A 2015-09-05 2015-09-04
A 2015-09-10 2015-09-04
B 2015-10-04 2015-10-04
B 2015-10-05 2015-10-04
B 2015-10-10 2015-10-04
You could use MIN with the OVER-clause
SELECT t.ColumnA,
DateCol = MIN( t.DateCol ) OVER ( PARTITION BY t.ColumnA ),
OtherColumns
FROM dbo.TableName t
you can go with a CROSS JOIN or FIRST_VALUE.
Declare #Yourtable table (groupCol varchar(25),firstDate date)
Insert into #Yourtable values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
SELECT a.*,b.firstDate
FROM #Yourtable a
CROSS JOIN (SELECT groupCol,MIN(firstDate) firstDate
FROM #Yourtable b
GROUP BY groupCol)b
WHERE a.groupCol =b.groupCol
OR
SELECT a.*,FIRST_VALUE(a.firstDate) OVER (PARTITION BY groupCol ORDER BY groupCol ASC) AS firstDate
FROM #Yourtable a

Resources