T-SQL - Track of task completion on time before Target - sql-server

I've been stuck on this task for the past couple of days and I could really use some help. What I'm trying to achieve is to check all the CHANGE_ID that were either not completed before its TARGET date. A CHANGE_ID would have multiple tasks under it and they all should be completed for it to be flagged as COMPLETED. If any of the tasks for a CHANGE_ID is still open or were not completed before TARGET date they it should be flagged as PENDING. TASK_DELAY_CAUSED_BY should display the first TASK_ID which either has missed the target or is still open.
CHANGE_ID
TASK_ID
CHANGE_TARGET
TASK_START
TASK_END
COMPLETED
PENDING
TASK_DELAY_CAUSED_BY
CRQ1
TAS001
2022-12-19
2022-12-17
2022-12-17
CRQ1
TAS002
2022-12-19
2022-12-17
2022-12-19
1
TAS002
CRQ1
TAS003
2022-12-19
2022-12-19
2022-12-21
CRQ2
TAS001
2023-01-13
2023-01-06
2023-01-07
CRQ2
TAS002
2023-01-13
2023-01-07
2023-01-08
CRQ2
TAS003
2023-01-13
2023-01-08
2023-01-11
1
CRQ3
TAS001
2023-03-26
2023-02-06
2023-02-07
CRQ3
TAS002
2023-03-26
2023-02-07
2023-02-11
1
CRQ4
TAS001
2023-04-02
2023-02-15
2023-02-15
CRQ4
TAS002
2023-04-02
2023-02-16
2023-02-18
CRQ4
TAS003
2023-04-03
2023-02-18
1
TAS003
Every CHANGE_ID would have 3 tasks in it. If TAS003 is not present then it will be considered as pending or still not completed. For instance CRQ3 does not have TAS003 hence it is flagged as PENDING and CRQ4 is still being worked on hence it is too flagged as pending.
Do you think it would be better to track the CHANGE_ID separately? All CHANGE_ID which has a TARGET date in the past with the upcoming ones. Please advise.
Data:
CREATE TABLE CRQ_TRACKER(CHANGE_ID nvarchar(20),TASK_ID nvarchar(20),CHANGE_TARGET date,TASK_START date,TASK_END date);
INSERT INTO CRQ_TRACKER (CHANGE_ID, TASK_ID, CHANGE_TARGET, TASK_START, TASK_END)
VALUES
('CRQ1', 'TAS001', '2022-12-19', '2022-12-17', '2022-12-17'),
('CRQ1', 'TAS002', '2022-12-19', '2022-12-17', '2022-12-19'),
('CRQ1', 'TAS003', '2022-12-19', '2022-12-19', '2022-12-21'),
('CRQ2', 'TAS001', '2023-01-13', '2023-01-06', '2023-01-07'),
('CRQ2', 'TAS002', '2023-01-13', '2023-01-07', '2023-01-08'),
('CRQ2', 'TAS003', '2023-01-13', '2023-01-08', '2023-01-11'),
('CRQ3', 'TAS001', '2023-03-26', '2023-02-06', '2023-02-07'),
('CRQ3', 'TAS002', '2023-03-26', '2023-02-07', NULL),
('CRQ4', 'TAS001', '2023-04-02', '2023-02-15', '2023-02-15'),
('CRQ4', 'TAS002', '2023-04-02', '2023-02-16', '2023-02-18'),
('CRQ4', 'TAS004', '2023-04-02', '2023-01-08', NULL);
For the CHANGE_IDs in the past I'm trying something as below but can't get my head around it
select
CHANGE_ID
,TASK_ID
,CHANGE_TARGET
,TASK_START
,TASK_END
,case when (CHANGE_TARGET between TASK_START and TASK_END)
and 1 = row_number() over(partition by CHANGE_ID,TASK_START order by TASK_START)
then null else 1 end COMPLETED
,case when (CHANGE_TARGET between TASK_START and TASK_END)
and 1 = row_number() over(partition by CHANGE_ID,TASK_START order by TASK_START)
then 1 else null end PENDING
from CRQ_TRACKER
where CHANGE_TARGET < getdate();

If you don't have a lot of columns, maybe this is fine. But, yes, typically I would separate the change_id from the task_id. You would do this to limit duplicate storage of information. I'd also make the key a simpler datatype, like an auto incrementing integer, for performance.
CHANGE_ID CHANGE_CODE CHANGE_TARGET
1 CRQ1 2022-12-19
TASK_ID TASK_CODE CHANGE_ID TASK_START TASK_END
1 TAS001 1 2022-12-17 2022-12-17
That's extra though. Using windowed queries, the final query might look something like this:
SELECT
c.CHANGE_CODE
,t.TASK_ID
,c.CHANGE_TARGET
,t.TASK_START
,t.TASK_END
--If a task is ended or is incomplete, mark as pending
,case when (t.TASK_END > c.CHANGE_TARGET OR t.TASK_END IS NULL)
THEN 'Pending' ELSE 'Complete' END AS TASK_STATUS
--Over the CHANGE_ID, if there are any tasks pending, make every CHANGE_STATUS "pending"
,case when
MAX(case when (t.TASK_END > c.CHANGE_TARGET OR t.TASK_END IS NULL)
THEN 1 ELSE 0 END) OVER (PARTITION BY c.CHANGE_ID) = 1
then 'Pending' ELSE 'Complete' END AS c.CHANGE_STATUS,
--Over the CHANGE_ID, take the first TASK_ID, ordered by the TASK_START
--with Complete orders blown out to the year 3000 to keep them at the
--bottom of the sort.
FIRST_VALUE(CASE WHEN (t.TASK_END > c.CHANGE_TARGET OR t.TASK_END IS NULL)
THEN t.TASK_ID END)
OVER (
PARTITION BY c.CHANGE_ID
ORDER BY
CASE WHEN (t.TASK_END > c.CHANGE_TARGET OR t.TASK_END IS NULL)
THEN t.TASK_START ELSE '30000101' END) AS TASK_DELAY_CAUSED_BY
from CHANGE_T AS c LEFT JOIN TASK_T AS t
ON c.CHANGE_ID = t.CHANGE_ID
where c.CHANGE_TARGET < getdate();

Related

How do I group results in columns using case statements?

I want to group the results in a separate column as Idle Total, Lock Total etc. I'm not a programmer and certainly not an db admin so i may not have all tools and access but I'll appreciate any help.
I've tried using other methods, here and other websites but it always comes down to the SUM or GROUP BY functions which I understand very little of.
SELECT TOP 10000
AGENT.AGENT_NAME,
ID.AGENT_ID,
TIME.DURATION_SECONDS,
SUM(CASE
WHEN ID.AGENT_ID IN ('IDLE','LOCK','LOGIN')
THEN 'WORKSTATE'
ELSE NULL
END)
FROM
WORKSTATE TIME
LEFT JOIN
DATABASE ID ON ID.INDEX = TIME.AGENT_INDEX
LEFT JOIN
DATABASE ORG ON TIME.AGENT_LOGIN_ID = ORG.AGENT_NAME
WHERE
(TIME.START_TIME = 2019-08-19) AND ORG.TEAM IN 'COMPANY'
GROUP BY
AGENT_AGENT_NAME,
ID.AGENT_ID,
TIME.DURATION_SECONDS
I would like the results to group TOTAL time an agent was Idle, Locked or Login on a given date.
Date Name Idle hrs Lock hrs Login
--- --- ------ ------ -----
08/19 John 2.3 1.7 4
You need to add CASE expression for each column, following query would help you to start with:
SELECT
TIME.START_TIME,
TIME.AGENT_LOGIN_ID,
SUM(CASE
WHEN ID.AGENT_ID = 'IDLE'
THEN TIME.DURATION_SECONDS END) as IdleTime, -- You may calculate You can formula to get HOURS from SECONDS or replace the column "DURATION_SECONDS" with the one that you looking to do aggregation
SUM(CASE
WHEN ID.AGENT_ID = 'LOCK'
THEN TIME.DURATION_SECONDS END) as LockTime,
SUM(CASE
WHEN ID.AGENT_ID = 'LOGIN'
THEN TIME.DURATION_SECONDS END) as LogTime
FROM WORKSTATE TIME
LEFT JOIN
DATABASE ID ON TIME.AGENT_INDEX = ID.INDEX
LEFT JOIN
DATABASE ORG ON TIME.AGENT_LOGIN_ID = ORG.AGENT_NAME
WHERE
(TIME.START_TIME = 2019-08-19) AND ORG.TEAM = 'COMPANY'
GROUP BY
TIME.START_TIME, TIME.AGENT_LOGIN_ID,

Query running tally of open issues on a given day

I have been banging my head on this for a while now and I think I've drastically over-complicating things at this point. What I have is a table containing the fields
OpenDate
ClosedDate
Client
Contract
Service
What I need to turn that into is
Date
Client
Contract
Service
OpenedOnThisDay
OpenedYesterday
ClosedOnThisDay
ClosedYesterday
OpenAtStartOfTomorrow
OpenAtStartOfToday
For any given Date, there may or may not be any issues opened or closed ON that day. That day should still be included with 0's
I have come at this a number of ways and can produce one of the desired results at a time (opened on, closed on, Open at end of), but I cannot get them all at once, at least not without exponentially increasing the query time.
My queries currently as views are as follows
Opened On
select Cast(EntryDateTime as Date) as DateStamp
,ContractNumber
,Client
,services.Service
,sum(1) as Count
,lag(sum(1)) OVER (
partition by tickets.ContractNumber
,services.Service ORDER BY Cast(EntryDateTime as Date) ASC
) as CountDayBefore
from v_JiraImpactedServices as services
LEFT JOIN v_JiraTickets as tickets ON services.ticketnumber = tickets.TicketNumber
WHERE tickets.Client is not null
AND tickets.TicketNumber IS NOT NULL
and tickets.ContractNumber is not null
GROUP BY Cast(tickets.EntryDateTime as Date)
,tickets.ContractNumber
,tickets.Client
,services.Service;
Closed On
select Cast(ResolvedDateTime as Date) as DateStamp
,ContractNumber
,Client
,services.Service
,sum(1) as Count
,lag(sum(1)) OVER (
partition by tickets.ContractNumber
,services.Service ORDER BY Cast(ResolvedDateTime as Date) ASC
) as CountDayBefore
from v_JiraImpactedServices as services
LEFT JOIN v_JiraTickets as tickets ON services.ticketnumber = tickets.TicketNumber
WHERE tickets.Client is not null
and tickets.TicketNumber is not null
AND tickets.ContractNumber is not null
GROUP BY Cast(tickets.ResolvedDateTime as Date)
,tickets.ContractNumber
,tickets.Client
,services.Service;
Open On
SELECT calendar.FullDate as DateStamp
,tickets.ContractNumber
,tickets.client
,services.Service
,IsNull(count(tickets.TicketNumber), 0) as Count
,IsNull(lag(count(tickets.TicketNumber), 1) OVER (
partition by tickets.ContractNumber
,services.Service Order By FullDate ASC
), 0) as CountDayBefore
FROM v_Calendar as calendar
LEFT JOIN v_JiraTickets as tickets ON Cast(tickets.EntryDateTime as Date) <= calendar.FullDate
AND (
Cast(tickets.ResolvedDateTime as Date) > calendar.FullDate
OR tickets.ResolvedDateTime is null
)
LEFT JOIN v_JiraImpactedServices as services ON services.ticketnumber = tickets.TicketNumber
WHERE tickets.Client is not null
AND tickets.ContractNumber is not null
GROUP BY calendar.FullDate
,tickets.ContractNumber
,tickets.Client
,services.Service;
As I said each of these by itself gives ALMOST the desired results, but omits days with 0 values.
Aside from producing days with 0 values, I need to also combine these into a single table result. All attempts so far have either produced obviously wrong JOIN results, or takes an hour to execute.
I would be most grateful if someone could point me in the right direction here.
Just to give you an idea, although the fieldnames don't match your scenario, this is how I would approach this:
WITH
SourceData (ClientID, ContractID, ServiceID, DateStamp) AS (
SELECT a.ID, b.ID, c.ID, d.DateStamp
FROM clients a
JOIN contracts b ON a.ID = b.ClientID
JOIN [services] c ON b.ID = c.ContractID
CROSS JOIN calendar d
WHERE d.DateStamp >= DATEADD(day, -60, GETDATE())
)
SELECT d.DateStamp, s.ClientID, s.ContractID, s.ServiceID
, COUNT(CASE WHEN Cast(EntryDateTime as Date) = d.DateStamp THEN 1 END) AS OpenedOn
, COUNT(CASE WHEN Cast(ResolvedDateTime as Date) = d.DateStamp THEN 1 END) AS ClosedOn
, COUNT(CASE WHEN Cast(ResolvedDateTime as Date) > d.DateStamp OR ResolvedDateTime IS NULL AND EntryDateTime IS NOT NULL THEN 1 END) AS InProgress
FROM SourceData s
LEFT JOIN tickets t
ON s.ClientID = t.ClientID
AND s.ContractID = t.ContractID
AND s.ServiceID = t.ServiceID
AND s.DateStamp >= Cast(EntryDateTime as Date)
AND (s.DateStamp <= ResolvedDateTime OR ResolvedDateTime IS NULL)
GROUP BY d.DateStamp, s.ClientID, s.ContractID, s.ServiceID

how to use count, case and Distinct together in sql server

I want to create a statement but i not success to complete this. can you please take a look and let me know what i need to do to complete this. My problem due how to add these two part in my query.
I want to look at the last 30 days of orders for each DRV1NUM. If that driver has worked 0 days then say ‘NOT ACTIVE’. If the driver has worked more than 15 days then ‘FULL TIME’ and less than 15 days is ‘PART TIME’.
In this one, I want to look at the last 30 days of orders and compare the left(4) of DRIVERNUM to the entire DRIVERNUM. In some instances, we have drivers where there is a 5th letter after the left 4.I want to look at the last 30 days of orders and if the left(4) DRV1NUM has more than one DRV1NUM WHEN looking at all characters, then SAY ‘MASTER’
SELECT DISTINCT DRVLICNUM,DOB,COUNTRY,CREDITLIMIT,DRIVERNUM=LEFT(DRIVERNUM,4),
SSN,D.VEHICLE,PHN1,DRVLICST,HOST,VEHICLE_MC,
VEHICLE_DOT,BACK_APPROVED=CASE WHEN PROBDATE IS NOT NULL THEN 'YES' ELSE 'NO' END
-- CASE WHEN COUNT(DISTINCT O.DROPDATE)=0 IN LAST 30 DAYS THEN 'NOT ACTIVE' WHEN COUNT(DISTINCT O.DROPDATE)>15 IN LAST 30 DAYS THEN 'FULL TIME' WHEN COUNT(DISTINCT O.DROPDATE)>=1 AND <=15 THEN 'PART TIME' IN LAST 30 DAYS ELSE NULL AS DAYSWORKED,
-- --CASE WHEN COUNT(DISTINCT O.DRV1NUM OF LEFT(DRIVERNUM,4 )>0 IN LAST 30 DAYS OF ORDERS>1 THEN 'MASTER IC' ELSE NULL AS MASTER
/* ABOVE TWO STATEMENT I WANT TO ADD */
FROM DRIVER D
FULL OUTER JOIN orde_ O ON O.DRV1ID=D.DRIVERID
AND ISNUMERIC(DRIVERNUM)=1 and DRIVERNUM NOT IN ('1010')
Expected Output That i want
DRVLICNUM Employee DOB COUNTRY ACTIVESTATUS
---------------------------------------------------------
055243324 CONTRACTOR 1985-04-13 ATLANTA FULL TIME
Here the ActiveStatus is Active because the driver worked more than 15 days in past 15 days or if it will less then 15 days it will be 'Part Time'
I don't have access to google drive link which you shared.
However, you will have to use CTE (common table expression) to get count of days and then use it compute value of ActiveStatus column.
Try using below code:
;
WITH CTE1
AS(
SELECT D.DRIVERID,
COUNT(DISTINCT O.DROPDATE) AS DayCount
FROM DRIVER AS D
LEFT JOIN ORDER AS O ON D.DRIVERID=O.DRV1ID AND O.DROPDATE BETWEEN CONVERT(DATE,DATEADD(DAY,-30,GETDATE())) AND GETDATE()
WHERE ISNUMERIC(D.DRIVERNUM)=1 AND D.DRIVERNUM NOT IN ('1010')
GROUP BY D.DRIVERID
)
SELECT DRVLICNUM,DOB,COUNTRY,CREDITLIMI,...
CASE
WHEN DayCount=0 THEN 'NOT ACTIVE'
WHEN DayCount<=15 THEN 'PART TIME'
WHEN DayCount>=30 THEN 'FULL TIME'
END AS ACTIVESTATUS
FROM CTE1 AS C
JOIN DRIVER AS D ON C.DRIVERID=D.DRIVERID
here is two siple queries for the first one:
1) select order by dates and group by driver (if there no more than one order per day):
select o.DRIVERID
, count(*) as cnt
from orde_ o
where o.DROPDATE betwen GETDATE() AND DATEADD(DAY,-30,GETDATE())
group by o.DRIVERID
2) select drivers and join groupped orders
select case
when o.ctn >= 15 then 'FULL TIME'
when o.ctn > 0 then 'PART TIME'
else 'NOT ACTIVE'
end
, <other fields you want>
from DRIVER as D
left join <query above> as o
on o.DRIVERID = d.DRIVERID
for the second one:
select left(d.DRIVERID, 4), count(*) as cnt
from DRIVER as D
group by left(d.DRIVERID, 4)
having count(*) > 1
and just join it, if is not null then compare whole id's

Using OVER() if customer has watch gladiator then 1 else 0 SQL SERVER

I think I need some guidance as to what is wrong in my query. I am trying to do
Watched_Gladiator=CASE WHEN FilmName IN (CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 END) then OVER(PARTITION BY Cust_Nr) THEN 1 ELSE 0 END
Tried this one too:
Watched_Gladiator=CASE WHEN FilmName IN (CASE WHEN FilmName LIKE '%Gladiator%' THEN Filmnamne END) then OVER(PARTITION BY Cust_Nr) THEN 1 ELSE 0 END
The Error I am currently getting is this:
Incorrect syntax near the keyword 'OVER'.
This is basically how my data looks like
Cust_Nr Date FilmName Watched Gladiator
157649306 20150430 Gladiator 1
158470722 20150504 Nick Cave: 20,000 Days On Earth 0
158467945 20150504 Out Of The Furnace 0
158470531 20150504 FilmA 0
157649306 20150510 Gladiator 1
158470722 20150515 Gladiator 1
I want to create a column (1 or zero) that shows if the customer has watched Gladiator then 1 ELSE 0. How can I do that?
I created a test column trying with a simple LIKE '%Gladiator%' THEN 1 ELSE 0. The problem with this solution is that it will show 1(one) more than once if the customer has watched multiple times. I only need 1 or zero.
I feel I am really close to finding a solution. I am very new to using OVER() and CASE WHEN but enjoying the thrill:=)
So you're saying that:
SELECT Cust_Nr, Date, FilmName,
CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 ELSE 0 END as WatchedGladiator
FROM YourTable
WHERE YourColumn = #somevalue
Doesn't work? Because according to the data you've given, it should.
EDIT:
Well based on Tim's comment, I would simply add this bit to the query.
SELECT Cust_Nr, Date, FilmName, WatchedGladiator
FROM
(
SELECT Cust_Nr, Date, FilmName,
CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 ELSE 0 END as WatchedGladiator
FROM YourTable
WHERE YourColumn = #somevalue
) as wg
WHERE WatchedGladiator = 1
The following does what you want for all films:
select r.*,
(case when row_number() over (partition by filmname order by date) = 1
then 1 else 0
end) as IsWatchedFirstAndGladiator
from results r;
For just Gladiator:
select r.*,
(case when filmname = 'Gladiator' and row_number() over (partition by filmname order by date) = 1
then 1 else 0
end) as IsWatchedFirst
from results r;
So you want to group by customer and add a column if this customer watched a specific film?
You could do:
SELECT Cust_Nr, MAX(Watched_Gladiator)
FROM( SELECT Cust_Nr,
Watched_Gladiator = CASE WHEN EXISTS
(
SELECT 1 FROM CustomerFilm c2
WHERE c2.Cust_Nr = c1.Cust_Nr
AND c2.FilmName LIKE '%Gladiator%'
) THEN 1 ELSE 0 END
FROM CustomerFilm c1 ) X
GROUP BY Cust_Nr
Demo
But it would be easier if you used the customer-table instead of this table, then you don't need the group-by.
Try grouping up to the cust/film level:
select
cust_nbr,
case when film_name like '%Gladiator%' then 1 else 0 end
from
(
select
cust_nbr,
film_name
from
<your table>
group by
cust_nbr,
film_name
) t
Or, as an alternative:
select distinct cust_nbr
from
<your table>
where
filmname = 'Gladiator'

Select Statement for 2 Tables

Hello everyone i have create 2 table in my SQL SERVER, the first table is called auction, the second bid, each auction has a status, they can be either valid, expired or unsold, when a auction is unsold, it does not have any bid, but when expired the auction should have one or bid, the problem is that i want to select every auction that has a bid and named it as successful and all auction which has status unsold as unsuccessful
here is my code
SELECT DATENAME(month, BID.Date) AS Years
,COUNT(DATENAME(Month, BID.Date)) AS Total
FROM Auction
INNER JOIN
BID ON Auction.AuctionID = BID.AuctionID
INNER JOIN
Item ON Auction.ItemID = Item.ItemID
WHERE (Auction.Status = 'Expired')
AND (BID.Status = 'Won')
GROUP BY DATENAME(month, BID.Date)
SELECT DATENAME(month, BID.Date) AS Years
, COUNT(DATENAME(Month, BID.Date)) AS Total
FROM Auction
INNER JOIN
BID ON Auction.AuctionID = BID.AuctionID
INNER JOIN
Item ON Auction.ItemID = Item.ItemID
WHERE (Auction.Status = 'UnSold')
GROUP BY DATENAME(month, BID.Date)
I want to group them by date.
I'm not a 100% sure I understand exactly what you asking. Maybe if you posted some sample data and a mock up the results set you want it would be easier.
Still, I think the key is you need to know the "result" (sold/unsold) of each auction. That can be done a few different ways, but Here's a relatively straightforward option to get you started
SELECT AuctionID
,ItemId
,CASE WHEN EXISTS(SELECT 1
FROM BID
WHERE BID.AuctionID=Auction.AuctionID)
THEN 'Won'
ELSE 'UnSold'
END Status
FROM Auction
EDIT:
One way to then get total would be to use something like the above as a CTE:
WITH AuctionResult AS (
SELECT CASE WHEN EXISTS(SELECT 1
FROM BID
WHERE BID.AuctionID=Auction.AuctionID)
THEN 'Won'
ELSE 'UnSold'
END [Status]
FROM Auction
) SELECT [Status]
,COUNT(*)
FROM AuctionResult
GROUP BY [Statush

Resources