Datediff on 2 rows of a table with a condition - sql

My data looks like the following
TicketID OwnedbyTeamT Createddate ClosedDate
1234 A
1234 A 01/01/2019 01/05/2019
1234 A 10/05/2018 10/07/2018
1234 B 10/04/2019 10/08/2018
1234 finance 11/01/2018 11/11/2018
1234 B 12/02/2018
Now, I want to calculate the datediff between the closeddates for teams A, and B, if the max closeddate for team A is greater than max closeddate team B. If it is smaller or null I don't want to see them. So, for example,I want to see only one record like this :
TicketID (Datediff)result-days
1234 86
and for another tickets, display the info. For example, if the conditions aren't met then:
TicketID (Datediff)result-days
2456 -1111111
Data sample for 2456:
TicketID OwnedbyTeamT Createddate ClosedDate
2456 A
2456 A 10/01/2019 10/05/2019
2456 B 08/05/2018 08/07/2018
2456 B 06/04/2019 06/08/2018
2456 finance 11/01/2018 11/11/2018
2456 B 12/02/2018
I want to see the difference in days between 01/05/2019 for team A, and
10/08/2018 for team B.
Here is the query that I wrote, however, all I see is -1111111, any help please?:
SELECT A.incidentid,
( CASE
WHEN Max(B.[build validation]) <> 'No data'
AND Max(A.crfs) <> 'No data'
AND Max(B.[build validation]) < Max(A.crfs) THEN
Datediff(day, Max(B.[build validation]), Max(A.crfs))
ELSE -1111111
END ) AS 'Days-CRF-diff'
FROM (SELECT DISTINCT incidentid,
Iif(( ownedbyteam = 'B'
AND titlet LIKE '%Build validation%' ), Cast(
closeddatetimet AS NVARCHAR(255)), 'No data') AS
'Build Validation'
FROM incidentticketspecifics) B
INNER JOIN (SELECT incidentid,
Iif(( ownedbyteamt = 'B'
OR ownedbyteamt =
'Finance' ),
Cast(
closeddatetimet AS NVARCHAR(255)), 'No data') AS
'CRFS'
FROM incidentticketspecifics
GROUP BY incidentid,
ownedbyteamt,
closeddatetimet) CRF
ON A.incidentid = B.incidentid
GROUP BY A.incidentid

I hope the following answer will be of help.
With two subqueries for the two teams (A and B), the max date for every Ticket is brought. A left join between these two tables is performed to have these information in the same row in order to perform DATEDIFF. The last WHERE clause keeps the row with the dates greater for A team than team B.
Please change [YourDB] and [MytableName] in the following code with your names.
--Select the items to be viewed in the final view along with the difference in days
SELECT A.[TicketID],A.[OwnedbyTeamT], A.[Max_DateA],B.[OwnedbyTeamT], B.[Max_DateB], DATEDIFF(dd,B.[Max_DateB],A.[Max_DateA]) AS My_Diff
FROM
(
--The following subquery creates a table A with the max date for every project for team A
SELECT [TicketID]
,[OwnedbyTeamT]
,MAX([ClosedDate]) AS Max_DateA
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
HAVING [OwnedbyTeamT]='A')A
--A join between view A and B to bring the max dates for every project
LEFT JOIN (
--The max date for every project for team B
SELECT [TicketID]
,[OwnedbyTeamT]
,MAX([ClosedDate]) AS Max_DateB
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
HAVING [OwnedbyTeamT]='B')B
ON A.[TicketID]=B.[TicketID]
--Fill out the rows on the max dates for the teams
WHERE A.Max_DateA>B.Max_DateB
You might be able to do with a PIVOT. I am leaving a working example.
SELECT [TicketID], "A", "B", DATEDIFF(dd,"B","A") AS My_Date_Diff
FROM
(
SELECT [TicketID],[OwnedbyTeamT],MAX([ClosedDate]) AS My_Max
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
)Temp
PIVOT
(
MAX(My_Max)
FOR Temp.[OwnedbyTeamT] in ("A","B")
)PIV
WHERE "A">"B"

Your sample query is quite complicated and has conditions not mentioned in the text. It doesn't really help.
I want to calculate the datediff between the closeddates for teams A, and B, if the max closeddate for team A is greater than max closeddate team B. If it is smaller or null I don't want to see them.
I think you want this per TicketId. You can do this using conditional aggregation:
SELECT TicketId,
DATEDIFF(day,
MAX(CASE WHEN OwnedbyTeamT = 'B' THEN ClosedDate END),
MAX(CASE WHEN OwnedbyTeamT = 'A' THEN ClosedDate END) as diff
)
FROM incidentticketspecifics its
GROUP BY TicketId
HAVING MAX(CASE WHEN OwnedbyTeamT = 'A' THEN ClosedDate END) >
MAX(CASE WHEN OwnedbyTeamT = 'B' THEN ClosedDate END)

Related

Need to pull customers with specific activity this year (Leads), where that activity (Lead) was their last one

I'm using SSMS to create a report showing customer accounts where the Sales Reps didn't follow up on leads we received this year. That would be indicated in accounts wheriin the list of activities (actions in the account), 'Lead' is the last one listed (the rep didn't take any actions after receiving the lead).
My code is pulling the latest 'Lead' activity for all customers who've had at least one lead this year:
CustomerName
Activity
Date
Bob's Tires
Lead
2021-01-05
Ned's Nails
Lead
2021-02-02
Good Eats
Lead
2021-02-03
I need it to only pull customers where the Lead was the last activity:
CustomerName
Activity
Date
Ned's Nails
Lead
2021-02-02
Here is my code and example tables. What am I missing? I've tried many things with no luck.
WITH activities AS (
SELECT
a. *
, CASE WHEN a.ContactDate = MAX(CASE WHEN a.Activity LIKE 'Lead%'
THEN a.ContactDate END) OVER (PARTITION BY a.AcctID)
THEN 1 ELSE 0 END AS no_followup
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM Customers c
INNER JOIN activities act ON c.AcctID = act.AcctID AND act.no_followup = 1
ORDER BY c.AcctID, act.ContactDate ASC
Table 1: Customers (c)
AcctID
CustomerName
11
Bob's Tires
12
Ned's Nails
13
Good Eats
14
Embers
Table 2: Activities (a)
AcctivityID
AcctID
Activity
Date
1
11
Contact Added
2021-01-01
2
11
Lead
2021-01-05
3
11
Phone Call
2021-01-06
4
12
Lead
2021-02-02
5
13
Lead
2021-02-03
6
13
Phone Call
2021-01-15
7
13
Sales Email
2021-01-15
8
14
Cold Call
2021-01-20
Your approach filters which rows may be considered in the max value in the comparison. I've included the suggested modification below which also modifies your CASE expression to consider that the current row is a lead as the case expression may filter the bounded values to consider (i.e. it will give you the latest lead activity but the latest lead activity may not be your latest activity).
Another modification, possibly optional but safe is adding the ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING in the OVER clause of your partition. While you could have also used UNBOUNDED PRECEDING instead of CURRENT ROW, it seems like extra processing when all the rows before the ordered ContactDate would be already be less than the current value and you are interested in the maximum value for contact date . The window function by default considers all rows current and before. The amendment would ask the window function to look at all the results in the partition after the current row.
Eg.
WITH activities AS
(
SELECT
a. *,
CASE
WHEN a.Activity LIKE 'Lead%' AND
a.ContactDate = (MAX(a.ContactDate) OVER (PARTITION BY a.AcctID ORDER BY a.ContactDate ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ))
THEN 1
ELSE 0
END AS no_followup
FROM
AcctActivities a
WHERE
a.ContactDate >= '2021-01-01'
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM
Customers c
INNER JOIN
activities act ON c.AcctID = act.AcctID
AND act.no_followup = 1
ORDER BY
c.AcctID, act.ContactDate ASC
Furthermore, if you are only interested in the customer details and all the resulting activity names would be Lead you may consider the following approach which uses aggregation and having clause to filter your desired results. This approach returns less details in the resulting CTE by filtering early.
WITH customers_with_last_activity_as_lead as (
SELECT AcctID, MAX(ContactDate) as ContactDate
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
GROUP BY AcctID
HAVING
MAX(a.ContactDate) = MAX(
CASE
WHEN a.Activity LIKE 'Lead%' THEN a.ContactDate
END
)
)
SELECT
c.Name,
-- 'Lead' as Activity, -- Uncomment this line if it is that you would like to see this constant value in your resulting queries.
act.ContactDate
FROM
Customers c
INNER JOIN
customers_with_last_activity_as_lead act ON c.AcctID = act.AcctID
ORDER BY
c.AcctID, act.ContactDate ASC
if all the values aren't a constant/literal Lead then the following approach may assist in retrieving the correct activity name also
WITH customers_with_last_activity_as_lead as (
SELECT
AcctID,
REPLACE(MAX(CONCAT(ContactDate,Activity)),MAX(ContactDate),'') as Activity,
MAX(ContactDate) as ContactDate
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
GROUP BY AcctID
HAVING
MAX(a.ContactDate) = MAX(
CASE
WHEN a.Activity LIKE 'Lead%' THEN a.ContactDate
END
)
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM
Customers c
INNER JOIN
customers_with_last_activity_as_lead act ON c.AcctID = act.AcctID
ORDER BY
c.AcctID, act.ContactDate ASC
Let me know if this works for you.
I agree with Gordon that your question isn't totally clear (do you actually only care about activities this year? What is the intended meaning of no_followup?). Having said that, I think this is what you want:
select c.Name,
lastActivity.Activity,
lastActivity.ContactDate
from Customers c
cross apply (
select top 1 a.Activity, a.ContactDate
from activities a
where a.acctId = c.acctId
-- and a.ContactDate >= '2021-01-01' uncomment this if you only care about activity this year
order by a.ContactDate desc
) lastActivity
where lastActivity.Activity like 'Lead%'

CASE condition and SUM() common table expressions or Group By

Hope some can help ? I have a 3rd party software were i can do custom sql querys but not able to change any of the database. Struggling to get the result i would like.
Do I use GROUP BY or CTE to get the result i need?
WITH CTE AS (
SELECT tblTicketsSummary.fldDateScheduled AS [Scheduled],
tblTicketsRow.fldStartTime AS [TIME], tblTicketsRow.fldPs AS
[s],tblTicketsRow.fldPrice AS PR,
tblTicketsSummary.fldFirstName+' '+tblTicketsSummary.fldLastName
AS [Client], ROW_NUMBER() OVER
(PARTITION BY tblTicketsSummary.fldTicketID ORDER BY
tblTicketsRow.fldTicketID) AS C, tblTicketsSummary.fldDateClosed
AS [Date Closed],
CASE WHEN tblTicketsRow.fldPs ='p' THEN tblTicketsRow.fldPrice
ELSE £0.00 END AS Product,
CASE WHEN tblTicketsRow.fldPs ='s' THEN tblTicketsRow.fldPrice
ELSE £0.00 END AS Services,
FROM tblTicketsSummary INNER JOIN
tblTicketsRow ON tblTicketsSummary.fldTicketID = tblTicketsRow.fldTicketID
WHERE (fldDateVoided IS NULL) AND (NOT(fldDateClosed IS NULL))
GetUserDate('AND','fldDateClosed','') AND tblTicketsRow.fldEmployeeName ='Tina Young'
AND tblTicketsSummary.fldTotal >1 )
SELECT * FROM CTE ORDER BY Scheduled ASC, TIME ASC
This give me the result below . I have removed Where c=1 in the last select which i will put back in the final query which give me the first result by time which is what i want. The bit i am struggling with is. I would like to add the prices of all products together and the prices of all service together to give a total of products and services sold. Then a grand total of services and product together in the final query .
Scheduled TIME s PR Client C Date Closed Product Services Total
17/07/2020 17:00 S £10.00 Ben Preston 1 17/07/2020 £0.00 £10.00 s+p
17/07/2020 17:45 S £1.00 Ben Preston 2 17/07/2020 £0.00 £1.00 s+p
17/07/2020 P £19.00 Ben Preston 3 17/07/2020 £19.00 £0.00 s+p
17/07/2020 P £10.00 Ben Preston 4 17/07/2020 £10.00 £0.00 s+p
The Result i would like is below. The final part of the query is as below. I am only able to custom query's in the 3rd party software.
SELECT * FROM CTE WHERE c=1 ORDER BY Scheduled ASC, TIME ASC
Scheduled TIME Client C Date Closed Product Services Total
17/07/2020 17:00 Ben Preston 1 17/07/2020 £29.00 £11.00 £40.00
I hope I have formatted this right for people to understand. Thank you for any help you can provide.
Seems like you simply need to add two Group Sums to your existing CTE and then add them in your final Select:
WITH CTE AS (
SELECT tblTicketsSummary.fldDateScheduled AS [Scheduled],
tblTicketsRow.fldStartTime AS [TIME],
tblTicketsRow.fldPs AS [s],tblTicketsRow.fldPrice AS PR,
tblTicketsSummary.fldFirstName+' '+tblTicketsSummary.fldLastName AS [Client],
ROW_NUMBER()
OVER (PARTITION BY tblTicketsSummary.fldTicketID
ORDER BY tblTicketsRow.fldStartTime) AS C,
tblTicketsSummary.fldDateClosed AS [Date Closed],
SUM(CASE WHEN tblTicketsRow.fldPs ='p' THEN tblTicketsRow.fldPrice ELSE £0.00 END)
OVER (PARTITION BY tblTicketsSummary.fldTicketID) AS Product,
SUM(CASE WHEN tblTicketsRow.fldPs ='s' THEN tblTicketsRow.fldPrice ELSE £0.00 END)
OVER (PARTITION BY tblTicketsSummary.fldTicketID) AS Services
FROM tblTicketsSummary
INNER JOIN tblTicketsRow
ON tblTicketsSummary.fldTicketID = tblTicketsRow.fldTicketID
WHERE (fldDateVoided IS NULL) AND (NOT(fldDateClosed IS NULL))
--GetUserDate('AND','fldDateClosed','') AND tblTicketsRow.fldEmployeeName ='Tina Young'
AND tblTicketsSummary.fldTotal >1 )
SELECT cte.*,
Product + Services as total
FROM CTE
WHERE C = 1
ORDER BY Scheduled ASC, TIME ASC;
Btw, the ORDER BY tblTicketsRow.fldTicketID on your ROW_NUMBER is not based on a unique column, thus the order of rows is not guaranteed. You probably want order by tblTicketsRow.fldStartTime instead.

Bring through a newly created calculated column in another query

I have 2 separate queries below which run correctly.Now I've created a calculated column to provide a count of working days by YMs and would like to bring this through to query1(the join would be query1.Period = query2.Yms)
please see the query and outputs below.
SELECT Client, ClientGroup, Type, Value, Period, PeriodName, PeriodNumber, ClientName
FROM metrics.dbo.vw_KPI_001_Invoice
select YMs,sum(case when IsWorkDay = 'X' then 1 else 0 end) from IESAONLINE.Dbo.DS_Dates
where Year > '2013'
group by YMs
Query 1
Client ClientGroup Type Value Period PeriodName PeriodNumber ClientName
0LG0 KarroFoods Stock 5691.68 201506 Week 06 2015 35 Karro Foods Scunthorpe
Query 2
YMs (No column name)
201401 23
Would the following work:
SELECT Client, ClientGroup, Type, Value, Period, PeriodName, PeriodNumber, ClientName, cnt
FROM metrics.dbo.vw_KPI_001_Invoice q1
INNER JOIN (select YMs,sum(case when IsWorkDay = 'X' then 1 else 0 end) as cnt from IESAONLINE.Dbo.DS_Dates
where Year > '2013'
group by YMs ) q2 ON q1.Period = q2.YMs
If a value isn't always available then you might consider changing the INNER JOIN to an OUTER JOIN.

Aggregating a sub query within query

I am currently working on aggregating the sum qty of "OUT" and "OUT+IN".
Current query is the following:
Select
a.Date
,a.DepartmentID
from
(Select
dris.Date
,dris.RentalItemKey
,dris.WarehouseKey
,ISNULL((Select TOP 1 dris.Date where OutQty=1 order by Date DESC),(Select ri.ReceiveDate from RentalItem ri where ri.RentalItemKey=dris.RentalItemKey)) as LastOutDate
,(Select d.DepartmentKey from Department d where d.Department=i.Department)as DepartmentID
, (CASE WHEN OutQty=1 OR (RepairQty=1 AND RentedQty=1) THEN 'IN' ELSE 'OUT' END) as Status
from DailyRentalItemStatus dris
inner join Inventory i on i.InventoryKey=dris.InventoryKey
where dris.Date='2014-08-02'
and i.ICode='3223700'
and i.Classification IN ('ITEM', 'ACCESSORY')
and i.AvailFor='RENT'
and i.AvailFrom='WAREHOUSE'
and dris.Warehouse='TORONTO')a
and I would like the result to be the following:
Date WarehouseID DepartmentID ICode Owned NotRedundant Out
2014-08-02 001T A00G 3223700 30 30 19
Where Owned is is The items with status as "OUT+IN", out is "OUT" and Not Redundant as where the lastout date is within the last 2 years from the date.
Help would be greatly appreciated.
I think this is close to what you're looking for. Your Not Redundant description, is hard to understand. Which dates are you comparing. The same trick for OUT may be used for that though.
My query also assumes that you always have a department connecting to the inventory table and that there's always a rentalitem.receivedate.
;WITH LastOut as
(Select Max(Date) as LastOutDate, rentalItemKey
from DailyRentalItemStatus
WHERE OutQty=1
)
Select
dris.Date
,dris.WarehouseKey as WarehouseID
,d.DepartmentKey as DepartmentID
, i.Icode
--,ISNULL((Select TOP 1 dris.Date where OutQty=1 order by Date DESC),(Select ri.ReceiveDate from RentalItem ri where ri.RentalItemKey=dris.RentalItemKey)) as LastOutDate
, Count(1) as Owned
, Sum(CASE WHEN NOT (OutQty=1 OR (RepairQty=1 AND RentedQty=1)) THEN 1 ELSE 0 END) as OUT
, Sum(CASE WHEN DateAdd(yy, 2,dris.[date]) >= ISNULL(lastout.lastoutdate, ri.ReceiveDate) then 1 else 0 end) as NonRedundent
from DailyRentalItemStatus dris
inner join Inventory i on i.InventoryKey=dris.InventoryKey
INNER JOIN Department d ON d.Department=i.Department
INNER JOIN RentalItem ri ON ri.RentalItemKey=dris.RentalItemKey
LEFT OUTER JOIN LastOUT ON LastOut.rentalItemKey=dris.RentalItemKey
where dris.Date='2014-08-02'
and i.ICode='3223700'
and i.Classification IN ('ITEM', 'ACCESSORY')
and i.AvailFor='RENT'
and i.AvailFrom='WAREHOUSE'
and dris.Warehouse='TORONTO'
Group BY dris.Date, d.DepartmentKey, Dris.WarehouseKey , i.icode

sql combining two queries and truncating date

I am trying to truncate dates to only get year/month as opposed to the form they are in which is year/month/day.time
What I want to do is count all of the cars that sold each month and all of the suvs that sold each month, having something like:
// counts cars
select SellDate, count(*)
from category
where machineIdentification = 1
GROUP BY SellDate
// counts suv's
select SellDate, count(*)
from category
where machineIdentification = 2
GROUP BY SellDate
Separately running each query gives me a list of dates (y/m/d/time to the second) and the number 1 because only 1 car or suv sold that exact time, however I am trying to group by SellDate and truncate the date so it only shows me the total numbers each month.
what I am trying to do is combine the queries and end up with values like so:
2009-01 23 10
2009-02 13 14
2009-03 29 7
With the first column being the year.month, the second being # of cars sold and the third being # of suv's sold
select
date(SellDate, 'start of month'),
SUM (CASE machineIdentification = 1 THEN 1 ELSE 0 END) AS carcount ,
SUM (CASE machineIdentification = 2 THEN 1 ELSE 0 END) AS suvcount
from category
where machineIdentification IN (1, 2 )
GROUP BY date(SellDate, 'start of month')
I'd concatenate the year/month in the client code so you can have "July 2010" for example
(I can't test in SQLLite, sorry, but this should be close except for output year/month format)
It could involve a union in a subquery and a format on the date, reformatted for SQLLite:
SELECT SellDate, COUNT(CARS) AS Cars, COUNT(SUVS) AS SUVS
FROM
(SELECT STRFTIME('%Y-%m',SellDate) AS SellDate,
MachineIdentification AS CARS, null AS SUVS
FROM Category
where machineIdentification = 1
UNION
SELECT STRFTIME('%Y-%m',SellDate) AS SellDate,
null AS CARS, MachineIdentification AS SUVS
FROM Category
where machineIdentification = 2
)
GROUP BY SellDate