Seems like I got another issue. The solution provided by DAVID P and Toner Mann worked well with some tickets but I do have a different ticket which does not work well with the join assigned_to = Assigned_from. Please check the attached sample data. I would need to find how long any user worked on the ticket. Also, assigned_to and assigned_from cannot be the same as always. sample dataIn the attached scenario James worked on the ticket for 61 days.
Thanks,
Naveen
Since you can't get good code formatting in a comment, I'm posting this as an answer. This is largely an extension of the answer by Tonner Mààn that rolls some calculations into the query. I tested this query on a local database containing the sample data from the question.
SELECT [Ticket],[AssignedTo],SUM([Days]) AS [Duration]
FROM
(
Select [Ticket],[AssignedTo],
DATEDIFF(Day, [Date], (Select MIN([Date])
from [TicketEvents] t2
where t2.[AssignedFrom] = t1.[AssignedTo]
AND t2.[Date] >= t1.[Date]
)) as [Days]
from [TicketEvents] t1
where Ticket = '22479-J8C4L3'
and Status = 'CASE_ASSIGNED'
) AS InnerQuery
GROUP BY [Ticket],[AssignedTo]
Result:
Ticket AssignedTo Duration
22479-J8C4L3 Christine 12
22479-J8C4L3 Phillips 4
22479-J8C4L3 Stacey 8
As I said in my other comment you need an algorythm that calculates the number of days between two dates, #DavidP noted that you also need a query to extract the dates to apply the algorythm on so here is something that may help:
Edit: Again thanks to #DavidP's idea, here is a code that should be calculating the difference in days:
with qry as (
Select [Ticket],
[Date] as startDate,
(Select MIN([Date])
from [yourTable] t2
where t2.[Assigned from] = t1.[Assigned to]
AND t2.[Date] >= t1.[Date]
) as endDate
from [yourTable] t1
where Ticket = '<value>'
and [Assigned To] = '<name>'
and Status = 'CASE_ASSIGNED'
)
select SUM(DATEDIFF(DAY,startDate,endDate)) as [Number of days] from qry
Obviously I did not test this code but it should give you the start and end dates to use to calculate the difference in days.
You may need to add a column with time (hour and minutes) for more precision.
Related
I currently have a query running to average survey scores for agents. We use the date range of the LastDayOfTheQuarter and 180 days back to calculate these scores. I ran into an issue for this current quarter.
One of my agents hasn't received any surveys in 2020 which is causing the query to not pull the current lastdayofquarter and 180 days back of results.
The code I am using:
SELECT
Agent,
U.Position,
U.BranchDescription,
(ADDDATE(LastDayOfQuarter, -180)) AS MinDate,
(LastDayOfQuarter) AS MaxDate,
COUNT(DISTINCT Response ID) as SurveyCount,
AVG(CASE WHEN Question ID = Q1_2 THEN Answer Value END) AS EngagedScore,
AVG(CASE WHEN Question ID = Q1_3 THEN Answer Value END) AS KnowledgableScore,
AVG(CASE WHEN Question ID = Q1_6 THEN Answer Value END) AS ValuedScore
FROM qualtrics_responses
LEFT JOIN date D
ON (D.`Date`) = (DATE(`End Date`))
LEFT JOIN `users` U
ON U.`UserID` = `Agent ID`
WHERE `Agent` IS NOT NULL
AND DATE(`End Date`) <= (`LastDayOfQuarter`)
AND DATE(`End Date`) >= (ADDDATE(`LastDayOfQuarter`, -180))
GROUP BY `Agent`, (ADDDATE(`LastDayOfQuarter`, -180))
i know the issue is due to the way I am joining the dates and since he doesn't have a result in this current year, the end date to date join isn't grabbing the desired date range. I can't seem to come up with any alternatives. Any help is appreciated.
I make the assumption that table date in your query is a calendar table, that stores the starts and ends of the quarters (most likely with one row per date in the quarter).
If so, you can solve this problem by rearranging the joins: first cross join the users and the calendar table to generate all possible combinations, then bring in the surveys table with a left join:
SELECT
U.UserID,
U.Position,
U.BranchDescription,
D.LastDayOfQuarter - interval 180 day AS MinDate,
D.LastDayOfQuarter AS MaxDate,
COUNT(DISTINCT Q.ResponseID) as SurveyCount,
AVG(CASE WHEN Q.QuestionID = 'Q1_2' THEN Q.Answer Value END) AS EngagedScore,
AVG(CASE WHEN Q.QuestionID = 'Q1_3' THEN Q.Answer Value END) AS KnowledgableScore,
AVG(CASE WHEN Q.QuestionID = 'Q1_6' THEN Q.Answer Value END) AS ValuedScore
FROM date D
CROSS JOIN users U
LEFT JOIN qualtrics_responses Q
ON Q.EndDate >= D.Date
AND Q.EndDate < D.Date + interval 1 day
AND U.UserID = Q.AgentID
AND Q.Agent IS NOT NULL
GROUP BY
U.UserID,
U.Position,
U.BranchDescription,
D.LastDayOfQuarter
Notes:
I adapted the date arithmetics - this assumes that you are using MySQL, as the syntax of the query suggests
You should really qualify all the columns in the query, by prefixing them with the alias of the table they belong to; this makes the query so much easier to understand. I gave a tried at it, you might need to review that.
All non-aggregated columns should appear in the group by clause (also see the comment from Eric); this is a a requirement in most databaseses, and good practice anywhere
So I can not use a temp table to accomplish this. Basically I need to create an experation date based off the next effective date available.
So for one proc code i have 10 fee schedules with only effective dates. I tried writing this but its not working because it is giving for some records the same expiration date. I know an order by is needed but i placed it in various places and it still gives me the same issue. Any help is greatly appreciated
SELECT a.*,
( CASE
WHEN (SELECT TOP 1 b.effectivedate - 1
FROM ietl_profileprocedure b
WHERE b.effectivedate > a.effectivedate
AND a.profilesid = b.profilesid) IS NULL THEN (SELECT
Dateadd(mm, Datediff(mm, 0, Getdate()) + 1, -1))
ELSE (SELECT TOP 1 b.effectivedate - 1
FROM ietl_profileprocedure b
WHERE b.effectivedate > a.effectivedate
AND a.profilesid = b.profilesid)
END ) AS 'ExpDate'
FROM ietl_profileprocedure a
WHERE profilesid = '4197'
AND procedurecode = '90685'
Thank you Juan I looked up lead and lag and was able to come up with the code below to suit my needs! now i can have a date something was posted and find the amount that should have pertained to the ProcedureCode at that time more easily
SELECT s.ProfileSID,s.ProcedureCode,s.Amount,cast(s.EffectiveDate as date) as EffectiveDate ,
cast(isnull(dateadd(day,-1,LEAD(EffectiveDate) OVER (ORDER BY ProfileSID,ProcedureCode,EffectiveDate
)),getdate())as date) ExpDate
FROM iETL_ProfileProcedure s
WHERE ProfileSID IN ('4197')and ProcedureCode = '90685'
I am looking for help with the following scenario:
I have an SQL Server DB, with a table that stores historical data. For example lets use the following as a sample set.
CAR, SERVICE DATE, FINDINGS
1234, 21/01/2001, Fuel Filter
1234, 23/09/2009, Oil Change
1234, 30/09/2015, Tyres
3456, 30/09/2015, Clutch
I would like from the following sample to bring back the result that shows the service of any car that was brought in on a give date, e.g. 30/09/2015 but only if it had an oil change in the past.
The query would only bring back:
1234, 30/09/2015, Tyres
since it is the only car on that date to be services that previously had an oil change.
Any help would be greatly appreciated.
Use an EXISTS clause:
SELECT cur.car,
cur.[service date],
cur.findings
FROM tablename cur
WHERE cur.[service date] = #mydate
AND EXISTS (
SELECT 1
FROM tablename past
WHERE past.car = cur.car
AND past.[service date] < cur.[service date]
AND past.findings = 'oil change'
)
I would do it as a cte.
Find the list of cars that have had oil changed, then join back onto the original table to find the intersection.
DECLARE #date datetime;
SET #date = '20150930'
with oil_Cte as (
select distinct car from
tableName
where findings = 'oil change'
and date < #date
)
select *
from tableName
inner join oil_cte on tableName.car = oil_cte.car
where date = #date
This question had many conditions like
One of the service should happen on specified date.
Previous service to that date shoule only be 'Oil Change'. If previous service is not 'Oil Change' then don't include it in result,
even if it meets condition 1. Along with this, if car has record of
OIL Chagne in past then it doesn't matter.
If car had OIL CHANGE record but don't have service on that date then it should not consider.
You can try this solution.. and here is the working SQLFiddle for you
select * from #t where car in (
select car from (
select car, [service date], findings, ROW_NUMBER() over (Partition by car order by [Service Date] desc) as [row]
from (select * from #t where car in (select car from #t where [service date] = '2015-09-30')) A) T
where T.row =2 and Findings = 'Oil Change'
) and [service date] = '2015-09-30'
Currently I have two tables, using Access 2007
TimeSheet(empID, TimeSheet, hours)
and
Rates(empID,Rate,PromotionDate)
How do I select the correct billing rates of employee based on their date of promotion?
For example, I have
Rates(001,10,#01/01/2013#)
Rates(001,15,#01/05/2013#)
Rates(002,10,#01/01/2013#)
and
Timesheet(001,#02/01/2013#,5)
Timesheet(001,#02/05/2013#,5)
Timesheet(002,#02/01/2013#,7)
In this case, I want to show that if empID 001 submited a time sheet at 02/01/2013 it would be billed with $10/hr
, but his timesheets starting at May 1st would be billed with $15/hr
My query right now is
SELECT t.empID , t.timesheet, r.hours ,
(SELECT rate FROM rate WHERE t.timeSheet >= r.promotionDate) AS RateBilled
FROM rate AS r , timesheet AS t
WHERE r.empid = t.empid
When ran, it shows a message of “At most one record can be returned by this subquery”
Any help would be appreciated, thanks.
Edit:
I have some strange output using the sql
SELECT t.empID, t.timesheet, r.rate AS RateBilled
FROM Rates AS r, timesheet AS t
WHERE r.empid=t.empid And t.timeSheet>=r.promotionDate
GROUP BY t.empID, t.timesheet, r.rate, r.promotionDate
HAVING r.promotionDate=MAX(r.promotionDate);
as you can see the output table ratebilled for empID 1 is switching back and forth from 10 to 15, even though past May 01, it should all be using 15 ,
any help is appreciated, thanks.
The select subquery you have setup potentially returns multiple values where only one should be returned. Consider the case where there may be two promotions and a recent timesheet, then the select will return two values because on both occasions the timesheet is newer than the promotion.
Try using the following as your subquery:
SELECT TOP 1 rate FROM rate
WHERE t.timeSheet >= r.promotionDate
ORDER BY r.promotionDate DESC
N.B. I don't think the above is terribly efficient. Instead try something like
SELECT t.empID , t.timesheet, r.hours , r.rate AS RateBilled
FROM rate AS r , timesheet AS t
WHERE r.empid = t.empid AND t.timeSheet >= r.promotionDate
GROUP BY t.empID, t.timesheet
HAVING r.promotionDate = MAX( r.promotionDate);
I have a query that checks a database to see if a customer has visited multiple times a day. If they have it counts the number of visits, and then tells me what times they visited. The problem is it throws "Tickets.lcustomerid" into the group by clause, causing me to miss 5 records (Customers without barcodes). How can I change the below query to remove "tickets.lcustomerid" from the group by clause... If I remove it I get an error telling me "Tickets.lCustomerID" is not a valid select because it's not part of an aggregate or groupby clause.
The Query that works:
SELECT Customers.sBarcode, CAST(FLOOR(CAST(Tickets.dtCreated AS FLOAT)) AS DATETIME) AS dtCreatedDate, COUNT(Customers.sBarcode) AS [Number of Scans],
MAX(Customers.sLastName) AS LastName
FROM Tickets INNER JOIN
Customers ON Tickets.lCustomerID = Customers.lCustomerID
WHERE (Tickets.dtCreated BETWEEN #startdate AND #enddate) AND (Tickets.dblTotal <= 0)
GROUP BY Customers.sBarcode, CAST(FLOOR(CAST(Tickets.dtCreated AS FLOAT)) AS DATETIME)
HAVING (COUNT(*) > 1)
ORDER BY dtCreatedDate
The Output is:
sBarcode dtcreated Date Number of Scans slastname
1234 1/4/2013 12:00:00 AM 2 Jimbo
1/5/2013 12:00:00 AM 3 Jimbo2
1578 1/6/2013 12:00:00 AM 3 Jimbo3
My current Query with the subquery
SELECT customers.sbarcode,
Max(customers.slastname) AS LastName,
Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME) AS
dtCreatedDate,
Count(customers.sbarcode) AS
[Number of Scans],
Stuff ((SELECT ', '
+ RIGHT(CONVERT(VARCHAR, dtcreated, 100), 7) AS [text()]
FROM tickets AS sub
WHERE ( lcustomerid = tickets.lcustomerid )
AND ( dtcreated BETWEEN Cast(Floor(Cast(tickets.dtcreated
AS
FLOAT)) AS
DATETIME
)
AND
Cast(Floor(Cast(tickets.dtcreated
AS FLOAT
)) AS
DATETIME
)
+ '23:59:59' )
AND ( dbltotal <= '0' )
FOR xml path('')), 1, 1, '') AS [Times Scanned]
FROM tickets
INNER JOIN customers
ON tickets.lcustomerid = customers.lcustomerid
WHERE ( tickets.dtcreated BETWEEN #startdate AND #enddate )
AND ( tickets.dbltotal <= 0 )
GROUP BY customers.sbarcode,
Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME),
tickets.lcustomerid
HAVING ( Count(*) > 1 )
ORDER BY dtcreateddate
The Current output (notice the record without a barcode is missing) is:
sBarcode dtcreated Date Number of Scans slastname Times Scanned
1234 1/4/2013 12:00:00 AM 2 Jimbo 12:00PM, 1:00PM
1578 1/6/2013 12:00:00 AM 3 Jimbo3 03:05PM, 1:34PM
UPDATE: Based on our "chat" it seems that customerid is not the unique field but barcode is, even though customer id is the primary key.
Therefore, in order to not GROUP BY customer id in the subquery you need to join to a second customers table in there in order to actually join on barcode.
Try this:
SELECT customers.sbarcode,
Max(customers.slastname) AS LastName,
Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME) AS
dtCreatedDate,
Count(customers.sbarcode) AS
[Number of Scans],
Stuff ((SELECT ', '
+ RIGHT(CONVERT(VARCHAR, dtcreated, 100), 7) AS [text()]
FROM tickets AS subticket
inner join
customers as subcustomers
on
subcustomers.lcustomerid = subticket.lcustomerid
WHERE ( subcustomers.sbarcode = customers.sbarcode )
AND ( subticket.dtcreated BETWEEN Cast(Floor(Cast(tickets.dtcreated
AS
FLOAT)) AS
DATETIME
)
AND
Cast(Floor(Cast(tickets.dtcreated
AS FLOAT
)) AS
DATETIME
)
+ '23:59:59' )
AND ( dbltotal <= '0' )
FOR xml path('')), 1, 1, '') AS [Times Scanned]
FROM tickets
INNER JOIN customers
ON tickets.lcustomerid = customers.lcustomerid
WHERE ( tickets.dtcreated BETWEEN #startdate AND #enddate )
AND ( tickets.dbltotal <= 0 )
GROUP BY customers.sbarcode,
Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME)
HAVING ( Count(*) > 1 )
ORDER BY dtcreateddate
I can't directly solve your problem because I don't understand your data model or what you are trying to accomplish with this query. However, I can give you some advice on how to solve the problem yourself.
First do you understand exactly what you are trying to accomplish and how the tables fit together? If so move on to the next step, if not, get this knowledge first, you cannot do complex queries without this understanding.
Next break up what you are trying to accomplish in little steps and make sure you have each covered before moving to the rest. So in your case you seem to be missing some customers. Start with a new query (I'm pretty sure this one has more than one problem). So start with the join and the where clauses.
I suspect you may need to start with customers and left join to tickets (which would move the where conditions to the left joins as they are on tickets). This will get you all the customers whether they have tickets or not. If that isn't what you want, then work with the jon and the where clasues (and use select * while you are trying to figure things out) until you are returning the exact set of customer records you need. The reason why you use select * at this stage is to see what in the data may be causeing the problem you are having. That may tell you how to fix.
Usually I start with a the join and then add in the where clasues one at a time until I know I am getting the right inital set of records. If you have multiple joins, do them one at time to know when you suddenly start have more or less records than you would expect.
Then go into the more complex parts. Add each in one at a time and check the results. If you suddenly go from 10 records to 5 or 15, then you have probably hit a problem. When you work one step at a time and run into a problem, you know exactly what caused the problem making it much easier to find and fix.
Group BY is important to understand thoroughly. You must have every non-aggregated field in the group by or it will not work. Think of this as law like the law of gravity. It is not something you can change. However it can be worked around through the use of derived tables or CTEs. Please read up on those a bit if you don't know what they are, they are very useful techniques when you get into complex stuff and you shoud understand them thoroughly. I suspect you will need to use the derived table approach here to group on only the things you need and then join that derived table to the rest of teh query to get the ontehr fields. I'll show a simple example:
select
t1.table1id
, t1.field1
, t1.field2
, a.field3
, a.MostRecentDate
From table1 t1
JOIN
(select t1.table1id, t2.field3, max (datefield) as MostRecentDate
from table1 t1
JOin Table2 t2 on t1.table1id = t2.table1id
Where t2.field4 = 'test'
group by t1.table1id,t2.field3) a
ON a.table1id = t1.table1id
Hope this approach helps you solve this problem.