calculation of expiration dates with sql - mysql

my first time doing a date calculation on my system
as you can see here on my first query how would i say that my expdate table is equal to this current date? then if so msgbox me "your item has expired"
on my second query i wanted to set a msgbox where msgbox me three months before my expdate ?
heres what i tried to do
cn.Open()
Dim query As String
query = "Select * from tblmeds where TIMESTAMPDIFF(MONTH,`expdate`,CURRENT_TIMESTAMP())< 1"
command = New MySqlCommand(query, cn)
readers = command.ExecuteReader
Dim count As Integer
count = 0
While readers.Read
count = count + 1
End While
cn.Close()
If count = 1 Then
msgbox "you have a expired items"
else
"no items are at risk"
PS:i am currently using PHPMYADMIN as my database

Assuming SQL SErver...
Subtract 3 months from the expiration date and compare that to the utc date (if multiple timezones are involved) otherwise you could just use getDate()
SELECT EXPDATE
FROM tblMeds
WHERE Dateadd(Month, -3,expDate) < = getutcdate()

If you change you query to
query = "Select count(*) as cnt from tblmeds where TIMESTAMPDIFF(MONTH,`expdate`,CURRENT_TIMESTAMP())< 1"
Then you won't need a look to count the rows, the server can do that which is faster.

Related

How to check only month and year in MySQL DateTime column in Vb.net

Im saving some calculated values in to database each month.Before saving, i want to check data is already available for this month and year. IF the same month exists, then user has to select another month or leaving without saving that. In Vb.net, im using DateTimepicker for selecting month and save that in DateTIme format in mysql. In that i want to check only month and year is existing.
Mysql:
1 2019-05-01 14:24:20 ProA 8.34 3.59
2 2019-05-01 14:24:20 ProB 9.21 5.54
Here record available for ProA for May2019 is available. So user cannot save for may 2019 again.
Dim selectedDate = DateTimePicker1.Value
Dim startDate = New Date(selectedDate.Year, selectedDate.Month, 1)
conn.Open()
sQuery = "SELECT * FROM riskanalysis WHERE DATE_FORMAT(reportdate,'%c %Y') >= #StartDate "
cmd_listview = New MySqlCommand(sQuery, conn)
cmd_listview.Parameters.AddWithValue("#StartDate", startDate)
Using reader As MySqlDataReader = cmd_listview.ExecuteReader()
If reader.HasRows Then
' User already exists
MsgBox("Record Already Exist for this Month!", MsgBoxStyle.Exclamation, "Select another month!")
Else
sQuery = "INSERT INTO riskanalysis (reportdate, process, avgrisk, avgriskafterImp) VALUES (#dat, #process, #avgrisk, #riskafterimp);"
For i As Integer = 0 To ProcessRiskGridView.Rows.Count - 1
cmd_listview = New MySqlCommand(sQuery, conn)
cmd_listview.Parameters.AddWithValue("dat", DateTimePicker1.Value)
cmd_listview.Parameters.AddWithValue("process", ProcessRiskGridView.Rows(i).Cells(0).Value)
cmd_listview.Parameters.AddWithValue("avgrisk", ProcessRiskGridView.Rows(i).Cells(1).Value)
cmd_listview.Parameters.AddWithValue("riskafterimp", ProcessRiskGridView.Rows(i).Cells(2).Value)
cmd_listview.ExecuteNonQuery()
Next
End Using
conn.Close()
I tried for some mysql command but it didnt work.
You don't need to Select * to find out if a record exists. Just get the count. Don't retrieve data you don't need. You certainly don't need a reader.
Keep your database objects local so you can be sure they are closed and disposed. `Using...End Using blocks will handle this for you even if there is an error.
Don't use .AddWithValue See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
You keep adding parameters to the collection over and over on each iteration. They only need to be added once. The value of #dat remains the same for all iterations. Only the values of the last 3 parameters change. You seem to be mixing up column names and parameter names. We are dealing with parameter names.
I have guessed at datatypes. Check your database for the actual datatypes and be sure to convert the values from the grid cells to the proper type if necessary. I don't know what kind of grid you are using and if it returns proper datatypes for .Value.
Example:
cmd.Parameters("#avgrisk") = CDbl(ProcessRiskGridView.Rows(i).Cells(1).Value)
Private Sub MySql()
Dim retVal As Integer
Dim selectedDate = DateTimePicker1.Value
Dim startDate = New Date(selectedDate.Year, selectedDate.Month, 1)
Dim sQuery = "SELECT Count(*) FROM riskanalysis WHERE DateTimeColumnName >= #StartDate; "
Using conn As New MySqlConnection("Your connection string")
Using cmd As New MySqlCommand(sQuery, conn)
cmd.Parameters.Add("#StartDate", MySqlDbType.DateTime).Value = startDate
conn.Open()
retVal = CInt(cmd.ExecuteScalar)
End Using
End Using
If retVal <> 0 Then
MessageBox.Show("Record Already Exist for this Month!")
Return
End If
sQuery = "INSERT INTO riskanalysis (reportdate, process, avgrisk, avgriskafterImp) VALUES (#dat, #process, #avgrisk, #riskafterimp);"
Using cn As New MySqlConnection("Your connection string")
Using cmd As New MySqlCommand(sQuery, cn)
With cmd.Parameters
.Add("#dat", MySqlDbType.DateTime).Value = selectedDate
.Add("#process", MySqlDbType.VarChar)
.Add("#avgrisk", MySqlDbType.Double)
.Add("#riskafterimp", MySqlDbType.Double)
End With
cn.Open()
For i As Integer = 0 To ProcessRiskGridView.Rows.Count - 1
cmd.Parameters("#process").Value = ProcessRiskGridView.Rows(i).Cells(0).Value
cmd.Parameters("#avgrisk") = ProcessRiskGridView.Rows(i).Cells(1).Value
cmd.Parameters("#riskafterimp") = ProcessRiskGridView.Rows(i).Cells(2).Value
cmd.ExecuteNonQuery()
Next
End Using
End Using
End Sub

How to remove fractions of seconds from a Date variable?

Background:
I read the pwdLastSet attribute from Active Directory, and want to store it in a DATETIME2(0) column in a SQL Server table. I don't want to store fractions of seconds.
It's an 8-byte Integer, expressed in 100-nanosecond steps since 12:00 AM, January 1, 1601. I use this function to convert it to a Date variable.
This continued to fail with ODBC-call failed, and it took me quite some time to figure out that the extra precision returned by that function caused the error.
Question:
What is the best way to remove fractions of seconds from a Date variable?
How to reproduce:
I use SQL Server 2008 R2 and Access 2010.
In SQL Server:
CREATE TABLE TestDT (
ID INT NOT NULL,
colDT2 DATETIME2(0) NULL,
CONSTRAINT PK_TestDT PRIMARY KEY (ID)
)
GO
INSERT TestDT (ID) VALUES (1)
GO
Link that table into Access, using Native Client ODBC driver or the current Microsoft ODBC Driver 17 for SQL Server. The default "SQL Server" driver doesn't really know how to work with DATETIME2.
In Access VBA:
Public Sub TestDT()
Dim DB As DAO.Database
Dim RS As DAO.Recordset
Dim dte As Date
Dim i As Long
Set DB = CurrentDb
' Random date+time
dte = CDate("2018-12-24 15:16:17")
' 1st iteration: write original date+time -> works
' 2nd iteration: try to write date+time with fractional seconds -> error for DATETIME2(0) column
For i = 1 To 2
If i = 2 Then
' Introduce milliseconds nastiness
dte = dte + 0.00001
End If
Debug.Print "Iteration " & i, Format(dte, "yyyy-mm-dd hh:nn:ss")
Set RS = DB.OpenRecordset("SELECT * FROM TestDT WHERE ID = 1", dbOpenDynaset)
With RS
.Edit
!colDT2 = dte
On Error Resume Next
.Update
If Err.Number <> 0 Then
Debug.Print "Error " & Err.Number, Err.Description
' The DAO Errors collection shows the actual error
Debug.Print Errors(0).Description
Else
Debug.Print "Update OK"
End If
On Error GoTo 0
.Close
End With
Next i
End Sub
Output:
Iteration 1 2018-12-24 15:16:17
Update OK
Iteration 2 2018-12-24 15:16:18
Error 3146 ODBC-Aufruf fehlgeschlagen.
[Microsoft][ODBC Driver 17 for SQL Server]Datetime field overflow.
Fractional second precision exceeds the scale specified in the parameter binding.
You could round off to the second this way:
PwdLastSetSecond = CDate(Int(PwdLastSet * 86400) / 86400)
I came up with
dte = CDate(Int(dte) + TimeSerial(Hour(dte), Minute(dte), Second(dte)))
but that's rather clumsy. :(
The correct function in Access to strip out the time part is:
DateValue(“date/time” expression)
So you thus get:
dte = DateValue (CDate("2018-12-24 15:16:17") )
or DateValue (whatever date time expression)
The correct function in Access to strip out the time part is:
TimeValue(“date/time” expression)
So you thus get:
dte = TimeValue (CDate("2018-12-24 15:16:17") )
or TimeValue (whatever date/ time expression)
If you need to save or strip out the time part into a separate value, then go:
dtMyTime = TimeValue ( (CDate("2018-12-24 15:16:17") )
However YOUR ISSUE is NOT stripping out Date, or time.
There is NO SUCH thing as “.ms” for the format.
That going to give you month + seconds.
If you look close:
2018-12-24 15:16:17.1217
In above, te 1217 is 12th month, and 17 seconds..
There is NO SUCH thing as “ms” for the format. So this is why you are seeing a overflow.
So you CAN NOT USE “.ms”.
You can only get the seconds in Access.
Just use a standard format command. If you want to strip out the "extra" time:
Set rst = CurrentDb.OpenRecordset("dbo_TimeTest2", dbOpenDynaset, dbSeeChanges)
rst.Edit
rst!StartTimeD = Format(rst!StartTimeD, "MM-DD-YYYY hh:nn:ss")
rst.Update
DANGER DANGER will Robinson. Access does not use or support “ms” in the format.
Simply using above “format” on the existing date/time you get will pull out and toss out the extra values after the seconds.

VB.NET How to create a reminder by checking database saved dates

I have this few line of codes where I save the date from datetimepicker to my database column REMINDER.
myCommand.CommandText = "Update kup_table SET REMINDER = #reminder Where ID = #theIDD"
myCommand.Parameters.AddWithValue("#reminder", DateTimePicker1.Value.Date)
myCommand.Parameters.AddWithValue("#theIDD", theID)
myCommand.ExecuteNonQuery()
In my database, the date is saved in this format 2015-12-14 00:00:00 since the datatype is DATETIME.
How do I compare it with the date now? If the saved date and today date are a match, then a reminder will go off.
I have tried using this sql command but having still having trouble where the reminder is always zero. Thanks in advance.
myCommand.CommandText = "Select COUNT(*) from kup_table Where REMINDER BETWEEN DATE() AND DATEADD('d', 1, DATE())"
mySqlConn.Open()
Console.WriteLine("Connected.")
count = myCommand.ExecuteScalar()
MsgBox("You have " + count.ToString + " reminder(s).")
Select COUNT(*) from kup_table Where DATE(REMINDER) = DATE(NOW())
The DATE() function returns the date part of the datetime only. (e.g. 2015-12-14)
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date

Returned Recordset is closed (mysql DB accessed via ODBC in VBA)

Long time viewer first time poster. I'm working on a database application with a front-end in Excel. Hence I am using VBA to access a MySQL database. The driver I'm using is the ODBC driver (Windows) and I have a valid connection to the database as lots of other queries function well.
What I'm trying to do is return the results from a series of SQL statements rather than trying to combine it all into one massive statement (which would probably work but be difficult to maintain/understand). I have added the flag FLAG_MULTI_STATEMENTS = 67108864 to the connection string which suppressed driver syntax errors.
But now when I run the following:
queryDB.Open SQLquery, conn
The recordset (queryDB) remains closed with no apparent error. The sql statement can be found here.
I may be generating errors that aren't being returned to VBA so any help here would be much appreciated.
NOTE: The sql statements work as I can paste that statement into phpMyAdmin and it returns the correct (non-empty) results. I don't know if the statements specifically are the problem but perhaps the use of CREATE TEMPORARY TABLE ... or the use of multiple statements in general.
Also I guess that the driver may be trying to return a result for each sql statement and VBA is only getting the first or something...
EDIT: the sql statement for future reference.
CREATE TEMPORARY TABLE tmpOrders AS
SELECT
o.customerName,
SUM(o.Sales) AS Sales,
SUM(TotalFobCost + TotalLandedCost + TotalLocalCost + TotalCmtCost) AS TotalCost,
YEAR(o.deliveryDate) AS YEAR,
MONTH(o.deliveryDate) AS MONTH
FROM devere_costing.orders_fixed_extras AS o
WHERE o.orderApproved = TRUE
AND o.orderCanceled = FALSE
AND o.deliveryDate BETWEEN '2014-01-01' AND '2014-03-31'
GROUP BY customerName, YEAR, MONTH
ORDER BY YEAR ASC, MONTH ASC, customerName ASC;
CREATE TEMPORARY TABLE tmpProj AS
SELECT p.customerName,
IF(p.MONTH > 9, p.YEAR, p.YEAR - 1) AS TrueYear,
1 + ((p.MONTH + 2) MOD 12) AS TrueMonth,
SUM(p.actualSalesInvoiced) AS salesInvoiced,
SUM(p.budget) AS budget
FROM devere_costing.sales_projection_data AS p
GROUP BY p.customerName, p.YEAR, p.MONTH
HAVING TrueYear BETWEEN YEAR('2014-01-01') AND YEAR('2014-03-31')
AND TrueMonth BETWEEN MONTH('2014-01-01') AND MONTH('2014-03-31');
CREATE TEMPORARY TABLE tmpLeft AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
LEFT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
CREATE TEMPORARY TABLE tmpRight AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
RIGHT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
(SELECT * FROM tmpLeft) UNION DISTINCT (SELECT * FROM tmpRight);
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop
Answer copied from the question body:
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop

Check Microsoft Access Form Values before Save

I have an Access Form - lets call it "Add Labor" (Access 2007) that saves data into a table.
The table has two columns in particular called "Start Date" and "End Date" (This table stores tasks)
There is also another table called FiscalYears which includes Start and End Dates for Fiscal Years, which is structured as follows
FyID
FYear
StartDate
EndDate
Example Data:
FYId FYear StartDate EndDate
-----------------------------
1 2010 10/1/2009 9/30/2010
2 2011 10/1/2010 9/30/2011
So in My Add Labor Form if someone enters labor that span across two fiscal years I need to enter two labor entries. Here is an example
If a user selects Labor Start Date = 6/30/2009
And End Date 10/2/2010 , it spans two fiscal years
So in my Labor Table I should enter two things
LaborID StartDate EndDate
-----------------------------
1 6/30/2009 9/30/2010
2 10/1/2010 10/2/2010
Basically I need to do a check before I save the record and add two records if they span Fiscal years, right now I'm just blindly doing Save Record on the form (inbuilt), but I guess I need to add some VBA. I've hardly ever used Access so this may be simple(hopefully). I am thinking instead of the event which just calls Save Record, I need it to add custom VBA.
Say you have an unbound form for adding the dates, you can say:
Dim rsFY As DAO.Recordset
Dim rsAL As DAO.Recordset
Dim db As Database
Dim sSQL As String
Set db = CurrentDb
''Select all years from the fiscal years table
sSQL = "SELECT FYear, StartDate, EndDate " _
& "FROM FiscalYears WHERE StartDate>=#" & Format(Me.StartDate, "yyyy/mm/dd") _
& "# Or EndDate <=#" & Format(Me.Enddate, "yyyy/mm/dd") _
& "# ORDER BY FYear"
Set rsFY = db.OpenRecordset(sSQL)
Set rsAL = db.OpenRecordset("AddLabor") ''table
''Populate recordset
rsFY.MoveLast
rsFY.MoveFirst
Do While Not rsFY.EOF
''Add records for each year selected
rsAL.AddNew
If rsFY.AbsolutePosition = 0 Then
rsAL!StartDate = Format(Me.StartDate, "yyyy/mm/dd")
Else
rsAL!StartDate = rsFY!StartDate
End If
If rsFY.AbsolutePosition + 1 = rsFY.RecordCount Then
rsAL!Enddate = Format(Me.Enddate, "yyyy/mm/dd")
Else
rsAL!Enddate = rsFY!Enddate
End If
rsAL.Update
rsFY.MoveNext
Loop
If the code was running in a main form with a subform showing the Addlabor table, you could update the subform to show the new records like so:
Me.Addlabor_subform.Requery
Why do you need a FiscalYears table? If your organization's fiscal years always start on Oct. 1 and end on Sept. 30, you can use a function to determine the fiscal year for a given date.
Public Function Fy(ByVal pDate As Date) As Integer
Dim intYear As Integer
Dim intReturn As Integer
intYear = Year(pDate)
If pDate > DateSerial(intYear, 9, 30) Then
intReturn = intYear + 1
Else
intReturn = intYear
End If
Fy = intReturn
End Function
And simple functions to return the Start and End dates for a given year.
Public Function FyStart(ByVal pYear As Integer) As Date
FyStart = DateSerial(pYear - 1, 10, 1)
End Function
Public Function FyEnd(ByVal pYear As Integer) As Date
FyEnd = DateSerial(pYear, 9, 30)
End Function
You can then determine how many fiscal years are included in a given date range by:
Fy(EndDate) - Fy(StartDate)
But I may be totally off base because you said "Start Date = 6/30/2009 And End Date 10/2/2010" spans two years. However, this expression returns 2 (3 years):
Fy(#10/2/2010#) - Fy(#6/30/2009#)