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'
Related
I am trying to select the max date in a table that has a Booking Date and a Written Premium value for that date. I want the newest date that has Written Premium (not equal to Zero).
In the above table I want, or expect the 4th Row in my query (7/28/2021, 330000), but I get the first row
(8/20/21, 0)
This is the query I run:
SELECT
MAX(booking_date) AS [Max Booking Date]
FROM
DW.dbo.Table1
GROUP BY
booking_year
HAVING
SUM(written_premium) <> 0
AND booking_year = '2021'
I think this is summing all the written premium, not over the rows so I am just getting the max booking date. Maybe I need a PARTITION BY function or something? I am stuck on this and would appreciate any help on how to best do this.
Thanks,
Brian
I think there are multiple options, but one could be:
SELECT TOP 1 booking_date, written_premium
FROM DW.dbo.Table1
WHERE written_premium <> 0
ORDER BY booking_date DESC
If all you want is the date then there is no need of group by and a HAVING clause.
Set your conditions in the WHERE clause:
SELECT MAX(booking_date) AS [Max Booking Date]
FROM DW.dbo.Table1
WHERE booking_year = '2021' AND written_premium <> 0;
If you want both columns:
SELECT TOP 1 booking_date AS [Max Booking Date], written_premium
FROM DW.dbo.Table1
WHERE booking_year = '2021' AND written_premium <> 0
ORDER BY booking_date DESC;
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.
I'm having a bit of an issue wrapping my head around the logic of this changing dimension. I would like to associate these two tables below. I need to match the Cost - Period fact table to the cost dimension based on the Id and the effective date.
As you can see - if the month and year field is greater than the effective date of its associated Cost dimension, it should adopt that value. Once a new Effective Date is entered into the dimension, it should use that value for any period greater than said date going forward.
EDIT: I apologize for the lack of detail but the Cost Dimension will actually have a unique Index value and the changing fields to reference for the matching would be Resource, Project, Cost. I tried to match the query you provided with my fields, but I'm getting the incorrect output.
FYI: Naming convention change: EngagementId is Id, Resource is ConsultantId, and Project is ProjectId
I've changed the images below and here is my query
,_cte(HoursWorked, HoursBilled, Month, Year, EngagementId, ConsultantId, ConsultantName, ProjectId, ProjectName, ProjectRetainer, RoleId, Role, Rate, ConsultantRetainer, Salary, amount, EffectiveDate)
as
(
select sum(t.Duration), 0, Month(t.StartDate), Year(t.StartDate), t.EngagementId, c.ConsultantId, c.ConsultantName, c.ProjectId, c.ProjectName, c.ProjectRetainer, c.RoleId, c.Role, c.Rate, c.ConsultantRetainer,
c.Salary, 0, c.EffectiveDate
from timesheet t
left join Engagement c on t.EngagementId = c.EngagementId and Month(c.EffectiveDate) = Month(t.EndDate) and Year(c.EffectiveDate) = Year(t.EndDate)
group by Month(t.StartDate), Year(t.StartDate), t.EngagementId, c.ConsultantName, c.ConsultantId, c.ProjectId, c.ProjectName, c.ProjectRetainer, c.RoleId, c.Role, c.Rate, c.ConsultantRetainer,
c.Salary, c.EffectiveDate
)
select * from _cte where EffectiveDate is not null
union
select _cte.HoursWorked, _cte.HoursBilled, _cte.Month, _cte.Year, _cte.EngagementId, _cte.ConsultantId, _cte.ConsultantName, _cte.ProjectId, _Cte.ProjectName, _cte.ProjectRetainer, _cte.RoleId, _cte.Role, sub.Rate, _cte.ConsultantRetainer,_cte.Salary, _cte.amount, sub.EffectiveDate
from _cte
outer apply (
select top 1 EffectiveDate, Rate
from Engagement e
where e.ConsultantId = _cte.ConsultantId and e.ProjectId = _cte.ProjectId and e.RoleId = _cte.RoleId
and Month(e.EffectiveDate) < _cte.Month and Year(e.EffectiveDate) < _cte.Year
order by EffectiveDate desc
) sub
where _cte.EffectiveDate is null
Example:
I'm struggling with writing the query that goes along with this. At first I attempted to partition by greatest date. However, when I executed the join I got the highest effective date for every single period (even those prior to the effective date).
Is this something that can be accomplished in a query or should I be focusing on incremental updates of the destination table so that any effective date / time period in the past is left alone?
Any tips would be great!
Thanks,
Channing
Try this one:
; with _CTE as(
select p.* , c.EffectiveDate, c.Cost
from period p
left join CostDimension c on p.id = c.id and p.Month = DATEPART(month, c.EffectiveDate) and p.year = DATEPART (year, EffectiveDate)
)
select * from _CTE Where EffectiveDate is not null
Union
select _CTE.id, _CTE.Month, _CTE.Year, sub.EffectiveDate, sub.Cost
from _CTE
outer apply (select top 1 EffectiveDate, Cost
from CostDimension as cd
where cd.Id = _CTE.id and cd.EffectiveDate < DATETIMEFROMPARTS(_CTE.Year, _CTE.Month, 1, 0, 0, 0, 0)
order by EffectiveDate desc
) sub
where _Cte.EffectiveDate is null
I have got DB table schema ActInfo like this
customerName customerEmail CertificateID Activated_On
A A#xxx.com xxxx 2013-05-20 04:02:39.000
A A#xxxx.com xxxxx 2013-09-11 03:09:34.000
A A#xxxx.com xxxxx 2013-04-03 06:09:34.000
We can see from above data that A has activated certificate three times in a year but i need to give a warning that he has activated three times per year
Is it possible with storedprocedure to check the count if user has activated the certificate more than twice in same year...Certificate ID are same or not it does not matter.
Would any one please help on this query
Many thanks for advance...
In this approach, I start with the most recent activation per customer, then outer join to all the activations for that customer within the past year. We ultimately return the customer and the count of activations within the past year.
SELECT
DerivedLastActivationByCustomer.CustomerName AS [Customer Name],
COUNT(ActInfo.CustomerName) AS [Activations in Past Year]
FROM
(
SELECT
CustomerName,
MAX(Activated_On) AS [Last Activation]
FROM
ActInfo
GROUP BY
CustomerName
) DerivedLastActivationByCustomer
LEFT OUTER JOIN ActInfo ON DerivedLastActivationByCustomer.CustomerName = ActInfo.CustomerName AND DATEDIFF(d, ActInfo.ActivatedOn, DerivedLastActivationByCustomer.[Last Activation]) < 365
GROUP BY
DerivedLastActivationByCustomer.CustomerName
Now, if you want to turn this into a stored procedure, you have options. You don't specify how this SP should work. In the simplest possible form, you could use just the above query and return the recordset.
Or, you could take the customer as an input parameter (i.e. #Cust), then use it as part of the WHERE clause of the inner query to only return info on that one specific customer.
Another possible approach would be to put a WHERE clause on the outermost SELECT statement to only return those with three or more activations (i.e. WHERE [Activations in Past Year] >= 3
Based on comments, the SP would be:
CREATE PROCEDURE [dbo].[GetActivationsInPriorYear]
(
#Cust nvarchar(max),
#ActivationCount int OUTPUT
)
AS
SELECT
DerivedLastActivationByCustomer.CustomerName AS [Customer Name],
#ActivationCount = COUNT(ActInfo.CustomerName) --AS [Activations in Past Year]
FROM
(
SELECT
CustomerName,
MAX(Activated_On) AS [Last Activation]
FROM
ActInfo
WHERE
ActInfo.CustomerName = #Cust
GROUP BY
CustomerName
) DerivedLastActivationByCustomer
LEFT OUTER JOIN ActInfo ON DerivedLastActivationByCustomer.CustomerName = ActInfo.CustomerName AND DATEDIFF(d, ActInfo.ActivatedOn, DerivedLastActivationByCustomer.[Last Activation]) < 365
GROUP BY
DerivedLastActivationByCustomer.CustomerName
GO
First off sorry for the poor subject line.
EDIT: The Query here duplicates OrderNumbers I am needing the query to NOT duplicate OrderNumbers
EDIT: Shortened the question and provided a much cleaner question
I have a table that has a record of all of the work orders that have been performed. there are two types of orders. Installs and Trouble Calls. My query is to find all of the trouble calls that have taken place within 30 days of an install and match that trouble call (TC) to the proper Install (IN). So the Trouble Call date has to happen after the install but no more than 30 days after. Additionally if there are two installs and two trouble calls for the same account all within 30 days and they happen in order the results have to reflect that. The problem I am having is I am getting an Install order matching to two different Trouble Calls (TC) and a Trouble Call(TC) that is matching to two different Installs(IN)
In the example on SQL Fiddle pay close attention to the install order number 1234567810 and the Trouble Call order number 1234567890 and you will see the issue I am having.
http://sqlfiddle.com/#!3/811df/8
select b.accountnumber,
MAX(b.scheduleddate) as OriginalDate,
b.workordernumber as OriginalOrder,
b.jobtype as OriginalType,
MIN(a.scheduleddate) as NewDate,
a.workordernumber as NewOrder,
a.jobtype as NewType
from (
select workordernumber,accountnumber,jobtype,scheduleddate
from workorders
where jobtype = 'TC'
) a join
(
select workordernumber,accountnumber,jobtype,scheduleddate
from workorders
where jobtype = 'IN'
) b
on a.accountnumber = b.accountnumber
group by b.accountnumber,
b.scheduleddate,
b.workordernumber,
b.jobtype,
a.accountnumber,
a.scheduleddate,
a.workordernumber,
a.jobtype
having MIN(a.scheduleddate) > MAX(b.scheduleddate) and
DATEDIFF(day,MAX(b.scheduleddate),MIN(a.scheduleddate)) < 31
Example of what I am looking for the results to look like.
Thank you for any assistance you can provide in setting me on the correct path.
You were actually very close. I realized that what you really want is the MIN() TC date that is greater than each install date for that account number so long as they are 30 days or less apart.
So really you need to group by the install dates from your result set excluding WorkOrderNumbers still. Something like:
SELECT a.AccountNumber, MIN(a.scheduleddate) TCDate, b.scheduleddate INDate
FROM
(
SELECT WorkOrderNumber, ScheduledDate, JobType, AccountNumber
FROM workorders
WHERE JobType = 'TC'
) a
INNER JOIN
(
SELECT WorkOrderNumber, ScheduledDate, JobType, AccountNumber
FROM workorders
WHERE JobType = 'IN'
) b
ON a.AccountNumber = b.AccountNumber
WHERE b.ScheduledDate < a.ScheduledDate
AND DATEDIFF(DAY, b.ScheduledDate, a.ScheduledDate) <= 30
GROUP BY a.AccountNumber, b.AccountNumber, b.ScheduledDate
This takes care of the dates and AccountNumbers, but you still need the WorkOrderNumbers, so I joined the workorders table back twice, once for each type.
NOTE: I assume that each workorder has a unique date for each account number. So, if you have workorder 1 ('TC') for account 1 done on '1/1/2015' and you also have workorder 2 ('TC') for account 1 done on '1/1/2015' then I can't guarantee that you will have the correct WorkOrderNumber in your result set.
My final query looked like this:
SELECT
aggdata.AccountNumber, inst.workordernumber OriginalWorkOrderNumber, inst.JobType OriginalJobType, inst.ScheduledDate OriginalScheduledDate,
tc.WorkOrderNumber NewWorkOrderNumber, tc.JobType NewJobType, tc.ScheduledDate NewScheduledDate
FROM (
SELECT a.AccountNumber, MIN(a.scheduleddate) TCDate, b.scheduleddate INDate
FROM
(
SELECT WorkOrderNumber, ScheduledDate, JobType, AccountNumber
FROM workorders
WHERE JobType = 'TC'
) a
INNER JOIN
(
SELECT WorkOrderNumber, ScheduledDate, JobType, AccountNumber
FROM workorders
WHERE JobType = 'IN'
) b
ON a.AccountNumber = b.AccountNumber
WHERE b.ScheduledDate < a.ScheduledDate
AND DATEDIFF(DAY, b.ScheduledDate, a.ScheduledDate) <= 30
GROUP BY a.AccountNumber, b.AccountNumber, b.ScheduledDate
) aggdata
LEFT OUTER JOIN workorders tc
ON aggdata.TCDate = tc.ScheduledDate
AND aggdata.AccountNumber = tc.AccountNumber
AND tc.JobType = 'TC'
LEFT OUTER JOIN workorders inst
ON aggdata.INDate = inst.ScheduledDate
AND aggdata.AccountNumber = inst.AccountNumber
AND inst.JobType = 'IN'
select in1.accountnumber,
in1.scheduleddate as OriginalDate,
in1.workordernumber as OriginalOrder,
'IN' as OriginalType,
tc.scheduleddate as NewDate,
tc.workordernumber as NewOrder,
'TC' as NewType
from
workorders in1
out apply (Select min(in2.scheduleddate) as scheduleddate from workorders in2 Where in2.jobtype = 'IN' and in1.accountnumber=in2.accountnumber and in2.scheduleddate>in1.scheduleddate) ins
join workorders tc on tc.jobtype = 'TC' and tc.accountnumber=in1.accountnumber and tc.scheduleddate>in1.scheduleddate and (ins.scheduleddate is null or tc.scheduleddate<ins.scheduleddate) and DATEDIFF(day,in1.scheduleddate,tc.scheduleddate) < 31
Where in1.jobtype = 'IN'