Logic statement within a loop for ASP Classic - asp-classic

What would be the logic statement to notify the user that they purchased the same product within the last 90 days?
SQL = "SELECT id, PurchaseDate, ProductName from TableName where id = " & id)
Do Until objRS.EOF
if (objRS("ProductName") and objRS("PurchaseDate") <= dateadd(day,-90,getdate())) and objRS("ProductName") and isNull(objRS("PurchaseDate")) then
AlreadyPurchased = true
end if
objRS.movenext()
Loop
My loop that writes it to the page:
<%=objrs("ProductName")%><% if AlreadyPurchased then%>Last Purchased with last 90 days<%end if %>

The best answer depends on a few assumptions:
A) your query does not return any NULL values, and
B) that you have previously stored the product name in a preexisting variable.
Your code has some issues.
1) if (objRS("ProductName") is basically asking if the fieldset value is a True boolean value. My first assumption is that you already know the product name that you're testing. So, the above snippet should be replaced with this: if (objRS("ProductName").value = strMyProductName where strMyProductName is a variable that stores the product name (as a string).
2) You should consider storing the calculated date in a variable outside the loop. No need to repeatedly compute the date if you can compute it once, store it in a variable, and use the variable in your comparison.
3) Remove the last component of your conditional and objRS("ProductName") because it's redundant and has the same faulty logic as what I explained in (1) above.
4) Your DateAdd() can be written better.
TL;DR
dim strMyProductName, dat90, AlreadyPurchased
AlreadyPurchased = false
strMyProductName = "Shoes"
dat90 = dateadd("d", -90, now) '90 days ago
Do Until objRS.EOF
if cstr(objRS("ProductName").value)=strMyProductName and cdate(objRS("PurchaseDate")) <= dat90 then
AlreadyPurchased = true
exit do
end if
objRS.movenext()
Loop

Related

Performing calculations based on multiple entries of data being fed from ViewModel

I'm having a bit of difficulty figuring out how to approach this function.
I have an SQLite database that is being handled by room, and I need to update entries in sharedpreferences based on this data. There is already a dao query setup to give entries by descending order of datetime:
#Query("SELECT * FROM data_table ORDER BY datetime DESC")
LiveData<List<Data>> getAllData();
These datetimes have corresponding float entries that I have to perform cumulative calculations on based on the difference of time to the next data entry's datetime. So, for example:
id datetime float
1 dt(1) 12.0f
2 dt(2) 15.0f
2 dt(3) 13.0f
I would start with
var timeDiff = ((Duration.between(LocalDateTime.parse(dt(1)),LocalDateTime.parse(dt(2)).toMillis())/1000).toFloat()
var currentValue = [big complicated formula applied to 12.0f based on timeDiff]
and then I would move on to
currentValue = (currentValue + 15.0f)
timeDiff = ((Duration.between(LocalDateTime.parse(dt(2)),LocalDateTime.parse(dt(3)).toMillis())/1000).toFloat()
currentValue = [big complicated formula applied to previous currentValue based on timeDiff]
until I get to the last entry, where I store that datetime and currentValue as a sharedpreference.
I'm not really sure how to go about this though. I was thinking of using a mutable array list:
mDataViewModel = ViewModelProvider(this).get(DataViewModel::class.java)
var tarray: MutableList<List<Data?>?> = ArrayList()
mDataViewModel.getAllData().observe(this, Observer<List<Data>>() {
fun onChanged(data: List<Data?>?) {
tarray.add(data)
}
})
Toast.makeText(this#MainActivity, tarray.toString(), Toast.LENGTH_SHORT).show()
and then performing the calculations on the list sequentially, but it doesn't appear to be populating. I would much prefer to perform the cumulative calculations as they're fed from the database. This is my first time using SQlite though, and I'm not there is a much easier way of accomplishing this than what I'm attempting to do. Any advice would be greatly appreciated.
I would much prefer to perform the cumulative calculations as they're fed from the database. This is my first time using SQlite though, and I'm not there is a much easier way of accomplishing this than what I'm attempting to do. Any advice would be greatly appreciated.
Perhaps consider the following as a pointer which may be along the lines of what you are trying to accomplish. That is getting the data you want from the database.
/* Create the TEST ENVIRONMENT */
DROP TABLE IF EXISTS data_table;
CREATE TABLE IF NOT EXISTS data_table (id INTEGER PRIMARY KEY, datetime TEXT, float REAL);
INSERT INTO data_table (datetime,float) VALUES('2021-04-01 12:00',12.0),('2021-04-01 13:00',15.0),('2021-04-01 16:00',13.0);
/* Show the raw/actual data */
SELECT * FROM data_table;
/* now extract the useful data */
WITH y AS (
SELECT
datetime,
float,
/* Get the float value of the previous row (as first would be NULL use COALESCE get the current float value )*/
COALESCE(
(SELECT float FROM data_table WHERE datetime < x.datetime ORDER BY datetime DESC LIMIT 1 ),x.float
) AS prev_float,
COALESCE(
(SELECT datetime FROM data_table WHERE datetime < x.datetime ORDER BY datetime DESC LIMIT 1 ),
(SELECT datetime FROM data_table ORDER BY datetime ASC LIMIT 1)
) AS prev_datetime
FROM data_table AS x
)
SELECT datetime, prev_datetime, strftime('%s',datetime) - strftime('%s',prev_datetime) AS datediff_in_seconds, float, float-prev_float as floatdiff FROM y ORDER BY datetime ASC;
DROP TABLE IF EXISTS data_table; /* clean-up the testing environment */
So you start off with the following data as an example :-
And it produces :-
i.e. the manipulated data
Note the use of COALESCE to return previous values for the first row. COALESCE returns the first non-null value of the listed values. The first being the value from the previous row, which would be null for the first row so it gets the current value
in the case of the date time, datetime could have been used however as cab be seen a sub query has been used just to demonstrate the flexibility of SQlite SQL.
It's then just a matter of two additional steps for Room :-
Create the Dao query using the complex query (from the WITH .... to the SELECT .... FROM y ORDER BY datetime ASC; )
Create a suitable POJO class/data class. For the example/pointer given you'd have var's or val's for
datetime
prev_datetime
datediff
float
prev_float
floatdiff
as these are the columns returned.
This isn't really about the database or the LiveData, right? You just want a way to run through this set of data?
You could use zipWithNext to turn the data into a list of Pairs, and run a fold on that. You gave an example of how you'd handle the first case, and how you'd handle every case after that, but really they're all
currentValue = currentValue + data1's float
timeDiff = time difference between data1 and data 2
currentValue = something based on currentValue and timeDiff
where currentValue starts off as 0, so on your first run it's currentValue = 0f + 12.0f
Here's how you can do that with a fold:
// Just a basic version of the data you're working with, as a demo
data class Thing(val time: DateTime, val number: Float)
typealias DateTime = Int
val stuff = listOf(Thing(10, 12.0f), Thing(12, 15.0f), Thing(15, 17.0f))
fun main() {
stuff.zipWithNext().fold(0f) { currentValue, (data1, data2) ->
val timeDiff = data2.time - data1.time
val newValue = complicatedTask(currentValue + data1.number, timeDiff)
newValue
}
.run(::println)
}
fun complicatedTask(number: Float, timeDiff: Int) = number * timeDiff
>> 117.0
The accumulator you're folding through is that currentValue float, so you start with 0f and return the new value at the end of the fold function, and that's what pops out at the end.
If you want the datetime too, you can make that part of the accumulator - I'm just gonna reuse that data class but you can make your own, or use a basic Pair or whatever:
stuff.zipWithNext().fold(Thing(0, 0f)) { (_, currentValue), (data1, data2) ->
val timeDiff = data2.time - data1.time
val newValue = complicatedTask(currentValue + data1.number, timeDiff)
Thing(data1.time, newValue)
}
>> Thing(time=12, number=117.0)
Using a start value with time=0 (gotta have something), and at the end of the function you're returning the first datetime, i.e. d(1) when you're comparing d(1) and d(2). You could return the other one if that's what you wanted. I just gave it an anonymous _ variable name in the function because you don't actually use it in there, you only care about it when it pops out as the result.
I'm not sure if that helps with what you're doing - if you want to keep running this process every time a new value is observed (maybe that's why you want the datetime in the result?) you could make the lambda a separate function, that way the fold can call it, but you can also use it yourself for one-off updates when you get some new data. And you can use that result as the start value for a new fold, if you need to run it on a bunch more values. Hope that makes sense!

Display grand total as the sum of averages (custom subtotal)

I have SQL Server query, which returns the following data:
I want to display the data in RDLC 2008 Report using matrix format which should give the following result:
The Grand Total of Qty field should return 12 for January & 14 for February.
I have tried many different methods one of which is by using the following expression in the matrix 'Qty' textbox :
=IIF(InScope("RowGroup_Category")
,IIF(InScope("RowGroup_SubCategory")
,Fields!Qty.Value
,Code.GetAverageMemberCount(Cint(Avg(Fields!Qty.Value)))
)
,Code.TotalMemberCount
)
The above functions are written in Report Properties Code as below:
Public Dim TotalMemberCount As Integer = 0
Function GetAverageMemberCount(ByVal AverageMemberCount As Integer) As Integer
TotalMemberCount = TotalMemberCount + AverageMemberCount
Return AverageMemberCount
End Function
I have also tried RunningValue(Fields!Qty.Value,Sum,"RowGroup_Category") and many such functions but I am unable to get the exact results. any help would be appreciated.. Thank you
Try adding this into a new column as a test:
RunningValue(Avg(Fields!MemberCount.Value,"RowGroup_Category"),SUM,Nothing)
If the value is correct you should be able to change SUM into MAX when setting this expression in the grand total field.
You can refer to the total like code.TotalMemberCount instead of using a get function
but i don't think you need this function in this case.
Check the following blog for a simular variable referencing situation
The only solution I could find that worked for me to solve this is to calculate the averages in a different dataset and use lookup functions to fill the grand total from there.
In your case I would add a key column in your original dataset:
select Category + '|' + Month as key, Category, SubCategory, Month, Qty, Amt
from YourTable
Create another dataset using:
select Category + '|' + Month as key, Category, Month, avg(Qty)
from YourTable
group by Category, Month
Add the second result as DataSet2 to the report. (In Visual Studio in the Report Data pane right click on DataSets.)
Add to the Report Properties -> Code section the following:
Function SumArray(varArray as array) as Decimal
Dim retVal As Decimal = 0
For Each item As Decimal In varArray
retVal = retVal + item
Next
Return retVal
End Function
Finally in the report use the following expression for Grand Total under Qty:
=code.SumArray(Lookupset(Fields!key.Value, Fields!key.Value, Fields!qty.Value, "DataSet2"))
P.S.: Make sure that the second dataset is also filled by your code the same way the original was.

Comparing Two Queries and Synchronizing Results

I have a table (Rooms) which contains a number of rows. Each row represents a room and each room needs to exist twice (once for fall, once for spring semester). Sometimes when folks add a room they only add it for one semester. I'm working on a process that will synchronize the rooms between semesters.
First I've pulled two queries, one that gets all of the rooms with fall in their semester column and one that gets all of the rooms with spring in their semester column, like so:
Dim getFallRooms = (From p In dbContext.Rooms _
Where p.semester = "Fall" _
Select p)
Dim getSpringRooms = (From p In dbContext.Rooms _
Where p.semester = "Spring" _
Select p)
The results will each contain multiple rows with the following columns: id, building, room, occupant, and semester.
What I want to do is something like this (pseudo):
For Each row in getFallRooms
If row.building and row.room not in getSpringRooms Then
' Code to add new row with fall as the semester.
End If
Next
Any suggestions on how I can make this into actual, workable code?
For Each row in getFallRooms
If (From p in getSpringRooms
Where p.building = row.building
AndAlso p.room = row.room).Count = 0 Then
' Code to add new row with fall as the semester.
End If
Next
You could do something quite similar to what's being done at this link.
I don't know precisely how to do it in VB.NET, but here's how I would do it in C#, using the LINQ extension methods:
var fallRooms = dbContext.Rooms.Where(room => room.semester.Equals("Fall")).Select(r => r.Name);
var nonSpringRooms = dbContext.Rooms.Where(room => room.semester.Equals("Spring"))
.Select(r => r.Name)
.AsQueryable()
.Except(fallRooms);
Afterwards, you could then do a For Each loop on nonSpringRooms to do whatever it is that you'd want to do.
On a side note, someone should correct me if I'm wrong, but I believe that the above code would only go to the database once. Hence, you'd get that benefit as well.
EDIT: I realized that since they'd be on the same table that the records would have the same primary key. I've subsequently changed the query above, assuming that you have a name field that would be the same in both records.

VBScript - Database - Recordset - How to pass DateDiff value in the database

I'm using DateDiff() function of ASP to find the date difference between two dates.
The Function works fine and displays the exact date difference between two dates but when it comes to insert this value in the database it takes 9 as the value irrespect of any date difference.
Suppose difference between two dates is more than 15 or 20 days, in the database it takes "9".
I have used INT as the DATA TYPE for the column where it displaying the date difference.
Is DATA TYPE creating an issue here?
I even tried using session variable to store the value but no luck - here's my code below:
if request.Form("sub") <> "" then
sql = "Select * from emp_leave_details"
rs.open sql , con, 1, 2
dim diff
dim todate
dim fromdate
fromdate= rs("leave_from")
todate= rs("leave_to")
session("date_diff")=datediff("d",fromdate,todate)
rs.addnew
rs("emp_name") = request.Form("name")
rs("emp_no") = request.Form("number")
rs("address") = request.Form("address")
rs("contact_no") = request.Form("contact")
rs("mobile_no") = request.Form("mobile")
rs("contact_onleave") = request.Form("contact_details")
rs("leave_type") = request.Form("rad")
rs("other_leave_details") = request.Form("PS")
rs("leave_from") = request.Form("from")
rs("leave_to") = request.Form("to")
rs("applied_by") = request.Form("apply")
rs("accepted_by") = request.Form("accept")
rs("approved_by") = request.Form("approve")
rs("no_of_leave_taken")= session("date_diff")
rs.update
response.Write("<script language='javascript'>{update();}</script>")
rs.close
end if
The datatype has nothing to do with this. Storing the value in the session is not the solution. You can use a regular variable.
From your code it looks like you always use the same values for fromdate and todate. This is because you do not iterate the rows in the resultset.
if not rs.bof and not rs.eof then
do while not rs.eof
'' code to execute for each row
rs.moveNext
loop
end if
In your current script rs will always return the results of the first row returned by the query.
The second problem your running into might be the Date datatype. Convert your value to a date using cDate and use this to calculate the difference.
Your problem is , you search for "Select * from emp_leave_details" which always gives all records from that table. You retrieve the values of the first record and do a diff of these, which results in always the same value, that is normal. From your question it is unclear what you really want to do. I suppose so want so select a record like
Select * from emp_leave_details where emp_name=<%=request.Form("name")%>
and based on that add a new record with a computed no_of_leave_taken.
Sorry guys my bad...
It was the database field name that I was calling instead of
fromdate= request.form("from")
todate= request.form("to")
I was calling this
fromdate= request.form("leave_from")
todate= request.form("leave_to")
Sorry again..but I really appreciate you all for providing me with all that possible solutions.
Thanks.

Get next number at insert, reset to 0 at new year

I am saving documents to database, each document has to have an id with the format YYYY-00000:
the first 4 characters are the current year
the second five characters are numbers. They start with 1 each year and then increment.
For example I could have these documents in my database: 2011-00001, 2011-00002, 2011-00003, 2012-00001, 2012-00002, ...
I am thinking something like this:
add two columns to table Documents (Year and Number)
Year is computed column, something like year(getdate())
Number is computed column, which gets value from a function GetNextNumberForCurrentYear
GetNextNumberForCurrentYear returns next number for the current year (for example select max(Number) + 1 from Documents where Year = year(getdate()), and some isnull checking)
But i fear, that two users could want to save the document at the same time and that they would receive the same Number. Is this possible? Any better ideas?
It is a ASP.NET C# web application, .NET 4.0, MSSQL 2005, I have the control over all the parts of the application.
PS: after insert I would like to return the Id of the new document to the user, so I would probably have to do something like: select Id from Documents where SomeId = scope_identity(), so I guess there should be an identity column somewhere...?
Edit (final solution): I get the next number from stored procedure, build the Id of the document (in format YYYY-00001) in .NET, save the whole document to the database (using TransactionScope for whole process) and then return the Id to the user.
create table DocumentNumbers ([Year] int not null, Number int not null default 1)
insert into DocumentNumbers ([Year], Number)
select 2012, 1 -- and more...
create procedure GetNextDocumentNumber
#year int
as
begin
declare #myResult table (nextNumber int)
update DocumentNumbers
set Number = isnull(Number, 0) + 1
output inserted.Number into #myResult
where [Year] = #year
select top 1 nextNumber from #myResult
end
You could create a table NumberSeries, which contains a column Year and a column CurrentNo and a function that returns the next number from it like the following:
DECLARE #myResult TABLE (nextNumber INT)
UPDATE NumberSeries
OUTPUT INSERTED.NextNo INTO #myResult
SET
CurrentNo = ISNULL(CurrentNo, 0) + 1
WHERE
Year = Year(GetDate())
DECLARE #result INT
#result = (SELECT TOP 1 nextNumber FROM #myResult)
RETURN #result
This updates the NumberSeries table atomically and inserts the new value into the #myResult table variable. After that, it returns the first (and only) value from the #myResult table variable.
Everything else, like SCOPE_IDENTITY() and such may cause errors when using triggers or in other cases - the solution using the OUTPUT clause is safe.
EDIT
As for returning the ID of the inserted document: this is basically the same thing.
DECLARE #myDocId TABLE (yr int, no int)
INSERT INTO Documents
OUTPUT INSERTED.Year , INSERTED.YearID INTO #myDocID
...
SELECT TOP 1
CAST(yr AS NVARCHAR) +
'_' +
RIGHT(REPLICATE('0', 5) + CAST(no AS NVARCHAR), 5) AS NewID

Resources