MS-ACCESS find data older than 3 years - ms-access

I am trying to have a push button that finds all records that are older than 3 years. I have a push button (named Expiring) on a combo box that searches a field Intro Date in table RECS. Here is what I have:
Private Sub Expiring_Click()
Call Search
End Sub
Sub Search()
Dim task As String
Me.Refresh
task = "SELECT * FROM RECS WHERE DateDiff('m', Date(), [Intro Date])" > 36
DoCmd.ApplyFilter task
End Sub
This does not work, I get a type mismatch for my task line. I simply want a push button that will only show data that is 3 years old. . .
I am super new to MS-ACCESS and SQL/VBA. This code was based off another function I created that searches if Intro Date older than a date specified in a unbounded box.

correct line of code
task = "SELECT * FROM RECS WHERE DateDiff('m', Date(), [Intro Date]) > 36"
You were comparing text to the number 36 the result is a boolean instead of a string

As stated by others, the issue with your code is that the comparison operator lies outside of the SQL statement -
task = "SELECT * FROM RECS WHERE DateDiff('m', Date(), [Intro Date])" > 36
Should be:
task = "SELECT * FROM RECS WHERE DateDiff('m', Date(), [Intro Date]) > 36"
DateDiff Considerations
When calculating temporal differences using the DateDiff function, consider that the choice of interval argument will impact the accuracy of the result.
For example, the intuitive solution might be to calculate the three-year range by supplying the DateDiff function with the yyyy interval and the two dates in question, and simply check whether the result is greater than 3:
DateDiff("yyyy", Date(), [Intro Date]) > 3
However, observe that this will omit all dates whose year components differ by 3 or less, not dates which are exactly 3 years or fewer apart - meaning that up to an extra year's worth of time may be excluded from your results:
?datediff("yyyy", #2018-01-01#, #2021-01-01#) ' 3 years apart
3
?datediff("yyyy", #2018-01-01#, #2021-12-31#) ' 3 years + 364 days apart
3
Similarly, if you opt for the month interval (m), the remainder of a month three years in future will also be excluded:
?datediff("m", #2018-01-01#, #2021-01-01#) ' 3 years apart
36
?datediff("m", #2018-01-01#, #2021-01-31#) ' 3 years + 30 days apart
36
Obviously we now descend into an infinite regression: if days are selected, the time component will be ignored, if hours are selected, the minutes component will be ignored, ad infinitum...
And so ultimately, the choice of interval depends upon the requirements of the application: if your application requires that something expire or be validated exactly 3 years to the day, then if using the DateDiff function, the day interval (d) should be used (and note that this would include leap days, and so the resulting date could be a day prior to the date 3 years earlier); if the remainder of the month is permitted, then the month interval (m) may suffice.

Related

Counting days in a product life excluding the summers

I am trying to write an ms access SQL query in order to calculate the number of days that have passed since a date in two year periods: "summer" (from 1/6 to 30/09) and non-summer (from 1/1 to 30/05 and from 1/10 to 31/12).
This date is the last "measure" of the product and it could be as old as two years.
Then I would like to know, as today, how many days of summer and vice-versa how many days of nonsummer has passed since the product has been measured.
ie. the date is 31/10/2017, I'd like two columns to show summer days passed (=30days*4months in 2018+ 28days in 2019 = 148days) and non-summer days passed ( 7months between Nov17 and may17+ 8 months from Ott18 to May 19 = 450days)
I have been trying using IIF cycles like that one:
(ANNI is a variable that means in which year we are 0=this year, 1 past year, etc)
days of non-summer
IIF(ANNI=0; "150"; IIF(ANNI=1;
IIF(G2.DATA_PRODUZ<#01/06/2018#;DateDiff("d",[G2]!
[DATA_PRODUZ],#01/06/2018#)+90+150;
IIF(G2.DATA_PRODUZ>#30/08/18#;DateDiff("d",[G2]!
[DATA_PRODUZ],#31/12/2018#)+150));"etc");)
But it won't work and it does it would only work this summer. I'd accept any ideas.
Perhaps not the most efficient method, but certainly the most readable might be to define a VBA function (in a public module) such as:
Function SummerDays(ByVal dateFrom As Date, ByVal dateTo As Date) As Long
While dateFrom < dateTo
If dateFrom >= DateSerial(Year(dateFrom), 6, 1) And dateFrom <= DateSerial(Year(dateFrom), 9, 30) Then
SummerDays = SummerDays + 1
End If
dateFrom = dateFrom + 1
Wend
End Function
And then call this from your query:
SummerDays(G2.DATA_PRODUZ, Date())
Then, for the non-summer days, you could either define another function with an inverse if statement, or subtract the number of summer days from the total number of days between the two dates, e.g.:
DateDiff("d",G2.DATA_PRODUZ, Date()) - SummerDays(G2.DATA_PRODUZ, Date())
To create a public module in which to define the above function:
Open the VBA IDE using Alt+F11
Insert a new public module by going to Insert > Module or Alt, I, M
Copy the above function definition into the Module, below the Option statements.
Save the Module with an appropriate name.

Access SQL - DateDiff Where it only looks for values that have existed over 2 weeks in the database

SELECT SerialNumber
FROM Warranty
WHERE (ABS(DateDiff('d', EndDate1, EndDate2)) > 7)
I currently have a query that shows me the Serial Numbers that have a difference of more than 7 days between the 2 dates. I want to add another condition where it will skip checking Serial Numbers that have been added to the database in the last 2 weeks
I'm just going to go ahead and add this as an answer, to make it clearer.
SELECT SerialNumber
FROM Warranty
WHERE (ABS(DateDiff('d', EndDate1, EndDate2)) > 7)
AND MyNewDateField >= DateAdd("d", -14, Date())
Essentially, this adds -14 days to the current date (or subtracts 2 weeks), and then only takes records where the DateAdded field (aka "MyNewDateField") is greater than, or equal to, that date. So, this will keep all records where DateAdded is within the most recent 2 weeks.

Convert "YYYYWW" into month (SSRS)

I am creating a report and would like to retrieve data based on a date field (string) in the format YYYYWW. I want to filter the data so I retrieve the current month, previous month, the month before that and older values - using 4 different datasets.
For example:
201547 = November 2015
How do I create a calculated member which converts a string into the month name and year? I would like to use this value to filter out the data from the cube. Is there any other way of doing this?
:]1
Try adding a calculated field and set it to this expression:
=MonthName(Month(DateAdd("ww",CInt(MID("201547",5,LEN("201547")))
,DateSerial(CInt(LEFT("201547",4)),1,1)))) & " " & LEFT("201547",4)
Then use that field to filter your report.
Replace the string for the field returning "201547"
=MonthName(Month(DateAdd("ww",
CInt(MID(Fields!YearWeek.Value,5,LEN(Fields!YearWeek.Value)))
,DateSerial(CInt(LEFT(Fields!YearWeek.Value,4)),1,1))))
& " " & LEFT(Fields!YearWeek.Value,4)
UPDATE: MDX script for querying a calulated member with the required format:
I've tested with the following MDX:
WITH MEMBER [Measures].[month&year] AS
'FORMAT(DateAdd("ww",STRTOVALUE(MID("201547",5,LEN("201547"))) ,CDATE(DateSerial(LEFT("201547",4),1,1))),"MMMM") + " " + LEFT("201547",4)'
SELECT { [month&year] } ON COLUMNS
FROM [Your Cube]
To use it in your query replace "201547" for [Dimension].[Attribute].MemberValue that corresponds to your cube structure:
WITH MEMBER [Measures].[month&year] as
'FORMAT(DateAdd("ww",STRTOVALUE(MID([Dimension].[Attribute].MemberValue,5,LEN([Dimension].[Attribute].MemberValue)))
,CDATE(DateSerial(LEFT([Dimension].[Attribute].MemberValue,4),1,1))),"MMMM") + " " + LEFT([Dimension].[Attribute].MemberValue,4)'
SELECT
{ [Dimension].[Attribute].[Attribute] }
{[month&year]} ON COLUMNS
FROM [Your Cube]
Once you have this field in your dataset you can easily filter the values using a parameter.
Let me know if this can help you.
Using a combination of the resources found out on the internet you can find the information you require.
First you need to find the first day of the first week of the year. This has already been determined by this answer as
DateAdd("d",
(-1) * (CInt(New DateTime(Year(Now), 1, 1).DayOfWeek) +
IIf(CInt(New DateTime(Year(Now), 1, 1).DayOfWeek) < DayOfWeek.Monday,
7,
0)
) + 1,
New DateTime(Year(Now), 1, 1))
This needs to be adapted for any year - not just the current one by finding the year from the string as follows
CInt(Left(Parameters!myDate.Value,4))
Now, as noted in this resource you can use a DateAdd to add the number of weeks of the year you have entered to the first date of the year
=(DateAdd(DateInterval.WeekOfYear,
CInt(Right(Parameters!myDate.Value,2))-1,
<<FIRST_DAY_OF_YEAR>>)
You then want to get the month name of the month of this date, using Month and MonthName. Smashing it all together would result in the following
=MonthName(Month(DateAdd(DateInterval.WeekOfYear,
CInt(Right(Parameters!myDate.Value,2))-1,
DateAdd("d",
(-1) * (CInt(New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1).DayOfWeek) +
IIf(CInt(New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1).DayOfWeek) < DayOfWeek.Monday,
7,
0)
) + 1,
New DateTime(CInt(Left(Parameters!myDate.Value,4)), 1, 1))
)))
This will get a Textual value of the month for the interval
Create a new placeholder in the same textbox and then repeat the above for finding the year
=Year(DateAdd(DateInterval.WeekOfYear, ...
(Remember to remove the last brace otherwise you’ll get a End of Statement expected error.)
This shoud give what you require, so
201501 = December 2014
201547 = November 2015
201553 = December 2015
201622 = May 2016
Let me know if this helps, or if you have further questions
Part 1: Establishing which day falls into which week
ISO-8601 declares the international standards for converting dates into week numbers, and vice-versa.
ISO standard weeks start on a Monday. A year can have either 52 or 53 weeks.
There are 4 ways of establishing when Week 1 starts:
- It is the first week with a majority (4 or more) of its days in January.
- Its first day is the Monday nearest to 1 January.
- It has 4 January in it. Hence the earliest possible dates are 29 December through 4 January, the latest 4 through 10 January.
- It has the year's first working day in it - i.e. excluding Saturdays, Sundays and the 1st of January.
Part 2: Excel calculations
This answer assumes that the dates are being stored as 6-character strings.
First determine the year and what day of the week it starts on.
1) Strip the first four characters from the string.`
=left(<cell containing the year+week string>, 4)
2) Prepend "1/1/" to it.
="1/1/"&left(<cell containing the year+week string>, 4)
3) Convert this string to an Excel date
=datevalue(<the last result>)
4) Determine the Weekday of the 1st day of that year.
=weekday(<the last result>)
5) Calculate the Excel date of the first Monday.
=if((<the last result>-4)<0,<the result of step 3>+8-<the last result>, <the result of step 3>+1-<the last result>)
6) Work out how many weeks on from that we are:
=right(<original 6 char year/week string>,2)
7) Convert 6) to a number
=value(<last result>)
8) Add that to the Monday of Week 1
=<result of step 5>+<result of step 7>*7
9) Convert this to a month number
=month(<last result>)
10) Convert this to a month name
=choose(<last result>,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
11) Add the year back on
=<lastresult>&" "&<result of 1>
This gives the month number which contains the Monday of the current numbered week.
If cell A1 contains '201547
then cell B1 should contain:
=CHOOSE(MONTH(IF((WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4)))-4)<0,DATEVALUE("1/1/"&LEFT(A1,4))+1-WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4))),DATEVALUE("1/1/"&LEFT(A1,4))+8-WEEKDAY(DATEVALUE("1/1/"&LEFT(A1,4))))+7*(VALUE(RIGHT(A1,2)))),"January","February","March","April","May","June","July","August","September","October","November","December")&" "&LEFT(A1,4)
If A1 contains 201547 then B1 returns "November 2015".
Further considerations:
You may want to make it more sophisicated, so that a month ending in a Monday does not have the week commencing with its final Monday counted as part of the month. More generally, that week only counts as part of a month is 3 or more of the days are in the month. Or 3 or more of its working days are part of the month.
As it is, I would sort with the calculated month and year as primary key and the YYYYMM code as secondary key.

SQL Month selection with two parameters

In a table I have a column called 'service_time' which is the start date of a service, and another column 'times_year' which is how many times per year the service shall be done.
The problem I have is how I should select the rows when -> (thisMonth == service_time (month)) OR (thisMonth == service_time (month) + 12/times_year)
The tricky part that I cannot solve is how should I do this if the 'times_year' is a value between 1-4? Without making 4 different OR's??
So should it be something of a loop decided by the 'times_year' instead of doing a OR query to check all 4 OR's every single time even if the 'times_year' isn't 4?
UPDATE:
Well I need help how to do a loop in the sql query statement wich will only select if the 'month' is correct, so basiclly now its the '08' and if the service_time is set to 2015-03-01 (day doesnt matter) it shall do a check with the times_year to check if 2015-03-01 shall be having a service > 2015-03 + 12/4(as an example) then 2015-06, 2015-09, 2015-12, 2016-03 Then no dont select and so on...
I could do this in PHP but it will not be efficent in the longer term
Hopefully, you don't have times_year values like 5, 11, and so forth. That will get a little nasty to work out. Values like 1,2,3,4,6,12 are good.
You can determine the next service time after the start time like this in MySQL
service_time + INTERVAL (12 DIV times_year) MONTH
Similarly, the third service time after the start time will work like this.
service_time + INTERVAL 3*(12 DIV times_year) MONTH
Maybe your business rules call for service to be due on or before the last calendar day of each month. You can display that like so.
LAST_DAY(service_time + INTERVAL 3*(12 DIV times_year) MONTH)
Finally, you may wish to display the next service due date after today (after CURDATE()) How do you do that?
First, it has been this many months since the first service time:
TIMESTAMPDIFF(MONTH, service_time, CURDATE())
Next, you want to know how many service intervals that covers. Easy:
times_year * TIMESTAMPDIFF(MONTH, service_time, CURDATE()) / 12.0
Next, you want the next highest service interval number:
CEIL(times_year * TIMESTAMPDIFF(MONTH, CURDATE(), service_time) / 12.0)
Finally, plug that into the next-service-time function:
service_time + INTERVAL
CEIL(times_year * TIMESTAMPDIFF(MONTH, service_time, CURDATE()) / 12.0)*
(12 DIV times_year) MONTH
It's a long formula, but it works.
Finally, you can use this WHERE clause to find out all the services due before the end of the present month.
WHERE LAST_DAY(service_time + INTERVAL
CEIL(times_year * TIMESTAMPDIFF(MONTH, service_time, CURDATE()) / 12.0)*
(12 DIV times_year) MONTH) <= LAST_DAY(CURDATE())

VBA: DateDiff Year off

I have the following code in VBA to return the year between two dates: DateDiff("yyyy", "10/10/1930","06/07/2008 8:30:00 AM")
It returns 78, but it should really be 77.
What is going on here?
VBA's DateDiff function was not designed to track elapsed time. That statement is simply evaluating the year.
See this msdn article, which offers a function to calculate the years elapsed: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvbadev/html/workingwithelapsedtime.asp
Function elapsed_years_function(first_date As Date, Optional second_date As Date = 0) As Integer
' This procedure is from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvbadev/html/workingwithelapsedtime.asp
Dim elapsed_years As Integer
If second_date = 0 Then
' Did the caller pass in a date? If not, use
' the current date.
second_date = Date
End If
elapsed_years = DateDiff("yyyy", first_date, second_date)
If second_date < DateSerial(Year(second_date), Month(first_date), Day(first_date)) Then
elapsed_years = elapsed_years - 1
End If
elapsed_years_function = elapsed_years
End Function
Edit: According to this (VB.Net) As pointed out by #Justin, this is for VB.Net, not Visual Basic, but the implementation is most likely identical for backwards compatibility. I've referenced the relevant VBScript documentation below for completeness.
Larger Intervals. If Interval is set
to DateInterval.Year, the return value
is calculated purely from the year
parts of Date1 and Date2. Similarly,
the return value for
DateInterval.Month is calculated
purely from the year and month parts
of the arguments, and for
DateInterval.Quarter from the quarters
containing the two dates.
For example, when comparing December
31 to January 1 of the following year,
DateDiff returns 1 for
DateInterval.Year,
DateInterval.Quarter, or
DateInterval.Month, even though at
most only one day has elapsed.
Also see this (VBScript):
When comparing December 31 to January
1 of the immediately succeeding year,
DateDiff for Year ("yyyy") returns 1
even though only a day has elapsed.
So it's likely implemented like this, which gives 78:
Year(SecondDate) - Year(FirstDate)
See #Ken's solution for an implementation that should work as intended.
If you did DateDiff("yyyy", "12/31/2010", "1/1/2011") it would return 1, even though there is only a day difference.
Working as designed: see Remarks (larger intervals) under http://msdn.microsoft.com/en-us/library/b5xbyt6f%28v=vs.80%29.aspx WHICH STATES:
If Interval is set to DateInterval.Year, the return value is calculated purely from the year parts of Date1 and Date2.
When you do datediff by year the operation performed is 2008 - 1930