Query to create records between two dates - sql-server

I have a table with the following fields (among others)
TagID
TagType
EventDate
EventType
EventType can be populated with "Passed Inspection", "Failed Inspection" or "Repaired" (there are actually many others, but simplifies to this for my issue)
Tags can go many months between a failed inspection and the ultimate repair... in this state they are deemed to be "awaiting repair". Tags are still inspected each month even after they have been identified as having failed. (and just to be clear, a “failed inspection” doesn’t mean the item being inspected doesn’t work at all… it still works, just not at 100% capacity…which is why we still do inspections on it).
I need to create a query that counts, by TagType, Month and Year the number of Tags that are awaiting repair. The end result table would look like this, for example
TagType EventMonth EventYear CountofTagID
xyz 1 2011 3
abc 1 2011 2
xyz 2 2011 2>>>>>>>>>>>>indicating a repair had been made since 1/2011
abc 2 2011 2
and so on
The "awaiting repair" status should be assessed on the last day of the month
This is totally baffling me...
One thought that I had was to develop a query that returned:
TagID,
TagType,
FailedInspectionDate, and
NextRepairDate,
then try and do something that stepped thru the months in between the two dates, but that seems wildly inefficient.
Any help would be much appreciated.
Update
A little more research, and a break from the problem to think about it differently gave me the following approach. I'm sure its not efficient or elegant, but it works. Comments to improve would be appreciated.
declare #counter int
declare #FirstRepair date
declare #CountMonths as int
set #FirstRepair = (<Select statement to find first repair across all records>)
set #CountMonths = (<select statement to find the number of months between the first repair across all records and today>)
--clear out the scratch table
delete from dbo.tblMonthEndDate
set #counter=0
while #counter <=#CountMonths --fill the scratch table with the date of the last day of every month from the #FirstRepair till today
begin
insert into dbo.tblMonthEndDate(monthenddate) select dbo.lastofmonth(dateadd(m,#counter, #FirstRepair))
set #counter = #counter+1
end
--set up a CTE to get a cross join between the scratch table and the view that has the associated first Failed Inspection and Repair
;with Drepairs_CTE (FacilityID, TagNumber, CompType, EventDate)
AS
(
SELECT dbo.vwDelayedRepairWithRepair.FacilityID, dbo.vwDelayedRepairWithRepair.TagNumber, dbo.vwDelayedRepairWithRepair.CompType,
dbo.tblMonthEndDate.MonthEndDate
FROM dbo.vwDelayedRepairWithRepair INNER JOIN
dbo.tblMonthEndDate ON dbo.vwDelayedRepairWithRepair.EventDate <= dbo.tblMonthEndDate.MonthEndDate AND
dbo.vwDelayedRepairWithRepair.RepairDate >= dbo.tblMonthEndDate.MonthEndDate
)
--use the CTE to build the final table I want
Select FacilityID, CompType, Count(TagNumber), MONTH(EventDate), YEAR(EventDate), 'zzz' as EventLabel
FROM Drepairs_CTE
GROUP BY FacilityID, CompType, MONTH(EventDate), YEAR(EventDate)`
Result set ultimately looks like this:
FacilityID CompType Count Month Year Label
1 xyz 2 1 2010 zzz
1 xyz 1 2 2010 zzz
1 xyz 1 7 2009 zzz

Here is a recursive CTE which generates table of last dates of months in interval starting with minimum date in repair table and ending with maximum date.
;with tableOfDates as (
-- First generation returns last day of month of first date in repair database
-- and maximum date
select dateadd (m, datediff (m, 0, min(eventDate)) + 1, 0) - 1 startDate,
max(eventDate) endDate
from vwDelayedRepairWithRepair
union all
-- Last day of next month
select dateadd (m, datediff (m, 0, startDate) + 2, 0) - 1,
endDate
from tableOfDates
where startDate <= endDate
)
select *
from tableOfDates
-- If you change the CTE,
-- Set this to reasonable number of months
-- to prevent recursion problems. 0 means no limit.
option (maxrecursion 0)
EndDate column from tableOfDates is to be ignored, as it serves as upper bound only. If you create UDF which returns all the dates in an interval, omit endDate in select list or remove it from CTE and replace with a parameter.
Sql Fiddle playground is here.

Related

Group dates by considering look ahead days

I am using SQL Server 2017. I have a table Requests, to make things simple, there's only one column RequestDate. For example,
RquestDate
4/11
4/12
4/13
4/16
4/18
I need to group by RequestDate by considering look ahead days. If look ahead day is 0, the result should be the same as raw table.
If look ahead day is 1, it means when I look at 4/11, I need to check if 4/12 exists, if so, group 4/12 into 4/11.
The result is:
4/11 --it groups 4/12
4/13
4/16
4/18
If look ahead day is 2, when looking at 4/11, it groups 4/12, 4/13 into it.
The result is:
4/11 -- group 4/12 and 4/13.
4/16 -- group 4/18
So this problem is different from the typical gap and island problem. Because when group dates, there could be gap there, e.g, when look ahead day is 2, 4/16 groups 4/17 and 4/18.
I tried some ways but can't find a decent solution.
A recursive common table expression could work.
Select start request date using a min() function.
Use that same date as the grouping start date.
Step 1 and 2 make up the recursion anchor / start row.
Recursively go looking for the next request date. This date is higher than the previous date (r.RequestDate > c.RequestDate) and does not have another row
that follow the same criteria before it (not exists ... r2.RequestDate < r.RequestDate).
If the current request date (from step 3) falls within the look ahead interval length, then maintain the grouping start date (then c.RequestGroupDate), otherwise start a new group on the current request date (else r.RequestDate).
Step 3 and 4 make up the recursive part of the CTE.
After the recursion every request date as a corresponding request grouping date. The group by r.RequestGroupDate clause reduces the result output to the distinct values.
Sample data
create table Requests
(
RequestDate date
);
insert into Requests (RequestDate) values
('2021-04-11'),
('2021-04-12'),
('2021-04-13'),
('2021-04-16'),
('2021-04-18');
Solution
declare #lookAhead int = 1; -- look ahead days parameter
with rcte as
(
select min(r.RequestDate) as RequestDate,
min(r.RequestDate) as RequestGroupDate
from Requests r
union all
select r.RequestDate,
case
when datediff(day, c.RequestGroupDate, r.RequestDate) <= #lookAhead
then c.RequestGroupDate
else r.RequestDate
end
from rcte c
join Requests r
on r.RequestDate > c.RequestDate
where not exists ( select 'x'
from Requests r2
where r2.RequestDate > c.RequestDate
and r2.RequestDate < r.RequestDate )
)
select r.RequestGroupDate
from rcte r
group by r.RequestGroupDate;
Result
For #lookAhead = 1:
RequestGroupDate
----------------
2021-04-11
2021-04-13
2021-04-16
2021-04-18
For #lookahead = 2:
RequestGroupDate
----------------
2021-04-11
2021-04-16
Fiddle to see things in action.

Crystal Reports sales report showing all dates even when not existing

I have been searching for answers on this for a couple of days but not found any useful results.
A bit of the backstory:
I have about ~20k items that i'm trying to do a lifetime sales history report on.Some items have history from 1/1/2005. Sales are only noted on dates they occur.
Trying to graph first lifetime of sales history by days, first 6mo of sales, and last 30 days of sales. I don't have permission to add a temp table, so i am working with importing an excel file. (and for some reason you can't right-outer-join it to invoice date. im guessing because it's external?)
My problem is that there HAS to be a simple way to tell crystal to include/print missing dates without a lookup table. I've already got the start and end dates passed to parameters.
Isn't there a way to dynamically generate missing dates between {?PM-Start} to {?PM-End}? Using a lookup table to check if all days from 1/1/2005-currentdate match is 90,000,000,000 extra bloops to check.
That would take hours to run. It should be able to grab minumum(sale_date), and maximum(sale_date) and plop a record for each day whether it exists in the DB or not. (How is this not already a basic function?)
Or am I just missing something super simple?
Isn't there any way to dynamically generate missing dates between {?PM-Start} to {?PM-End}? Using a lookup table to check if all days from 1/1/2005-currentdate match is 90,000,000,000 extra bloops to check.
By using a query like below you can generate a calendar view to fill missing date:
;with years(yyyy) as (
select 2005
union all
select yyyy + 1
from years
where yyyy < datepart(year, getdate())
), months(mm) as (
select 1
union all
select mm + 1
from months
where mm < 12
), allDays(dd) as (
select 1
union all
select dd + 1
from allDays
where dd < 31
), calendar as (
select --datefromparts(y.yyyy, m.mm, d.dd) [date]
cast(cast(y.yyyy as varchar(4))+'-'+cast(m.mm as varchar(2))+'-'+cast(d.dd as varchar(2)) as date) [date]
from allDays d
cross join
months m
cross join
years y
where isdate(cast(y.yyyy as varchar(4))+ '/'+cast(m.mm as varchar(2))+'/'+cast(d.dd as varchar(2))) <> 0
)
select *
from calendar;
SQL Fiddle Demo

SQL loop or alternative

I am trying to gather a timestamp by day of turnover, it has to be run live so I cant store the data.
So the basic principal is that I need to see when a job was created, and then see if the delivery date is in a certain period. This then gives me a result. I then need to loop that for the total number of days in the month.
The code below works in a fashion in that It gathers the data, but it doesn't spit it out in 1 extract.
I need a procedure that will gather a time stamp function on various data
DECLARE #a int;
Set #a = 1
While #a <=31
Begin
Select
'2016-10-' + convert(varchar(3),#a),
Sum(mjob.Value) as value
From jobdetails as mjob
Where mjob.createDate <= '2016-10-' + convert(varchar(3),#a)
--this has to increment based on the #a
AND mjob.DeliveryTime between '2016-10-01' and '2016-11-01'--this stays the same throughout the given month
Set #a = #a +1
END
SUM() can also be used as a window function, with a <window frame preceding> to get cumulative sums.
At least on MS Sql Server 2012 and beyond.
select createDate,
sum(Value) over (partition by year(createDate), month(createDate) order by createDate rows unbounded preceding) AS CumulativeValue
from JobDetails
where CreateDate between '2016-10-01' and '2016-10-31'
and DeliveryTime between '2016-10-01' and '2016-11-01';

how to skip a day to previous in sql database if that day produce no files

i need to print the files From the current day plus from the day before...but if the day before is sunday then i want to print the files from saturday
i have a database containing a files of a month..each day produces some files but sunday is a holiday and at that day no files are generated.now i have done the query to pick the files in such a way that giving current date will results the files of that day and previous day. But the problem is on monday when i give the current date it produces files of monday and sunday... i dont want to print files of sunday as it is null,instead of that when am giving the current date if its is monday i should get the files of saturday instead of monday.
this is the code am using now
select files from table1 where [date]>=DATEADD(DAY,DATEDIFF(DAY,0,GETDATE()-1),0) ";
except for sundays i have entered the files for each day
insert into dbtable(date,files) values('date value',' filename');
Assuming your are using SQL Server (and not MySQL), you can use a CROSS APPLY to get data for the last two [dates] with files:
SELECT *
FROM dbo.table1 o
CROSS APPLY ( SELECT TOP 2 [date]
FROM dbo.table1 i
GROUP BY [date]
WHERE [date]>=DATEADD(DAY,DATEDIFF(DAY,0,GETDATE()-5),0)
ORDER BY [date] DESC
) det
WHERE det.[date] = o.[date]
Look for both dates, the current and the one before. From these records take the newer one.
select files
from table1
where date =
(
select max(date)
from table1
where date in (getdate(), getdate() - interval 1 day)
);
(Updated regarding Code-Monk's commment. Thanks.)
This is standard SQL except for the date manipulation which is MySQL specific. If you were mistaken with the DBMS tagged, then replace the part where a day is subtracted from current day with what's appropriate for your DBMS.)
UPDATE: You want to select from two days, the currect day and the last date before. As there should be no future records, this always means the two maximum dates in your table.
The slightly altered query using ORDER BY and LIMIT:
select files
from table1
where date in
(
select date
from table1
order by date desc limit 2
);
Try MySQL function DAYNAME(date1) to check for day is sunday,If so then get difference of two days instead of one.
mysql> SELECT DAYNAME(GETDATE());
+-----------------------+
| DAYNAME('2008-05-15') |
+-----------------------+
| Thursday |
+-----------------------+
1 row in set (0.01 sec)
If you are using SQL server,you can try datename function to get the day name:
datename(dw,getdate())
Try this for SQL Server:
SELECT files from table1
where [date]>=DATEADD(DAY,case when datename(dw,(GETDATE()-1))='SUNDAY' THEN -2 ELSE -1 END,getdate());
For MySQL Server Try:
SELECT files from table1
where `date`>=DATE_SUB(current_Date,interval (case when DAYNAME(date_sub(current_date,interval 1 day))='SUNDAY' THEN 2 ELSE 1 END) day);
Hope this works, because I don't have mysql here to test (only SQL Server):
SELECT files FROM table1
WHERE CAST([date] AS DATE) IN (CURDATE() - CASE DATEPART(dw,GETDATE()) WHEN 2 /* Monday */ THEN 2 ELSE 1 END, CURDATE())
The idea is to test if the date (as date only, no time part) falls in a specific list of dates, being today and yesterday or Saturday.

SQL Server Retrieving Recurring Appointments By Date

I'm working on a system to store appointments and recurring appointments. My schema looks like this
Appointment
-----------
ID
Start
End
Title
RecurringType
RecurringEnd
RecurringTypes
---------------
Id
Name
I've keeped the Recurring Types simple and only support
Week Days,
Weekly,
4 Weekly,
52 Weekly
If RecurringType is null then that appointment does not recur, RecurringEnd is also nullable and if its null but RecurringType is a value then it will recur indefinatly. I'm trying to write a stored procedure to return all appointments and their dates for a given date range.
I've got the stored procedure working for non recurring meetings but am struggling to work out the best way to return the recurrences this is what I have so far
ALTER PROCEDURE GetAppointments
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS
SELECT
appointment.id,
appointment.title,
appointment.recurringType,
appointment.recurringEnd,
appointment.start,
appointment.[end]
FROM
mrm_booking
WHERE
(
Start >= #StartDate AND
[End] <= #EndDate
)
I now need to add in the where clauses to also pick up the recurrences and alter what is returned in the select to return the Start and End Dates for normal meetings and the calculated start/end dates for the recurrences.
Any pointers on the best way to handle this would be great. I'm using SQL Server 2005
you need to store the recurring dates as each individual row in the schedule. that is, you need to expand the recurring dates on the initial save. Without doing this it is impossible to (or extremely difficult) to expand them on the fly when you need to see them, check for conflicts, etc. this will make all appointments work the same, since they will all actually have a row in the table to load, etc. I would suggest that when a user specifies their recurring date, you make them pick an actual number of recurring occurrences. When you go to save that recurring appointment, expand them all out as individual rows in the table. You could use a FK to a parent appointment row and link them like a linked list:
Appointment
-----------
ID
Start
End
Title
RecurringParentID FK to ID
sample data:
ID .... RecurringParentID
1 .... null
2 .... 1
3 .... 2
4 .... 3
5 .... 4
if in the middle of the recurring appointments schedule run, say ID=3, they decide to cancel them, you can follow the chain and delete the remaining ID=3,4,5.
as for expanding the dates, you could use a CTE, numbers table, while loop, etc. if you need help doing that, just ask. the key is to save them as regular rows in the table so you don't need to expand them on the fly every time you need to display or evaluate them.
I ended up doing this by creating a temp table of everyday between the start and end date along with their respective day of the week. I limited the recurrence intervals to weekdays and a set amount of weeks and added where clauses like this
--Check Week Days Reoccurrence
(
mrm_booking.repeat_type_id = 1 AND
#ValidWeeklyDayOfWeeks.dow IN (1,2,3,4,5)
) OR
--Check Weekly Reoccurrence
(
mrm_booking.repeat_type_id = 2 AND
DATEPART(WEEKDAY, mrm_booking.start_date) = #ValidWeeklyDayOfWeeks.dow
) OR
--Check 4 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 3 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*4) = 0
) OR
--Check 52 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 4 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*52) = 0
)
In case your interested I built up a table of the days between the start and end date using this
INSERT INTO #ValidWeeklyDayOfWeeks
--Get Valid Reoccurence Dates For Week Day Reoccurences
SELECT
DATEADD(d, offset - 1, #StartDate) AS [Date],
DATEPART(WEEKDAY,DATEADD(d, offset - 1, #StartDate)) AS Dow
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY s1.id) AS offset
FROM syscolumns s1, syscolumns s2
) a WHERE offset <= DATEDIFF(d, #StartDate, DATEADD(d,1,#EndDate))
Its not very elegant and probably very specific to my needs but it does the job I needed it to do.

Resources