Problems with Recordset Filter - asp-classic

I'm having trouble with a filter on an ADO Recordset in legacy ASP Classic code, and I'm trying to understand if what I'm trying to do is not supported, or if I'm just doing it wrong.
I have a recordset of Items, and they have a Status of 1 (active) or 0 (inactive), and an optional End_Date. In my administrative user interface, I have a control to show all items or only those that should be displayed to end-users: Status = 1 AND ( End_Date is null OR End_Date > Date() )
To implement that logic, I tried:
rs.Filter = "Status = 1 AND ( End_Date = null OR End_Date > #" & Date() & "# )"
but I get
ADODB.Recordset (0x800A0BB9)
Unknown runtime error
After much fooling around, it seems that ADO doesn't like the grouping parens around the End_Date conditions in combination with the AND condition. If I take the parens out, this works:
rs.Filter = "Status = 1 AND End_Date = null OR End_Date > #" & Date() & "#"
But that's just an accident -- it looks like the filter conditions are evaluated in order, and so I get the results I want. If I change the AND to OR, the parens work:
rs.Filter = "Status = 1 OR ( End_Date = null OR End_Date > #" & Date() & "# )"
But of course that logic is wrong -- it shows Active but expired items.
Strangely, if I move the conditions around, it breaks again:
rs.Filter = "End_Date = null OR Status = 1 AND End_Date > #" & Date() & "# "
crashes with the same ADODB error.
I can't seem to predict what will and won't work, and the docs I've read are very sketchy on the syntax expected (it's not pure T-SQL!), the limitations, etc. and all the examples I've seen have at most two conditions. I don't think my conditions are all that complex. Can anyone tell me if what I'm trying to do is supported, if there's a better way to do it, or point me to comprehensive docs and samples that match this kind of logic?
Thanks!

ADO Recordset Object Filter Property:
There is no precedence between AND and OR. Clauses can be grouped within parentheses. However, you cannot group clauses joined by an OR and then join the group to another clause with an AND, like this:
(LastName = 'Smith' OR LastName =
'Jones') AND FirstName = 'John'
Instead, you would construct this
filter as:
(LastName = 'Smith' AND FirstName =
'John') OR
(LastName = 'Jones' AND FirstName =
'John')
So you would have to construct your filter like this:
rs.Filter = "( Status = 1 AND End_Date = null ) OR ( Status = 1 AND End_Date > #" & Date() & "# )"

I know that you are working with legacy code and probably other things are working, but how do you open the recordset in this specific page? Are you using some constant defined in adovbs.inc?
For example:
rs.Open "SELECT * FROM table1", db, adOpenStatic, adLockPessimistic

What happens when you try this...
Dim strToday
strToday = Now()
rs.Filter = "Status=1 AND (End_Date = null OR End_Date > """ & strToday & """)"

Related

How to execute a complex sql statement and get the results in an array?

I would like to execute a fairly complex SQL statement using SQLite.swift and get the result preferably in an array to use as a data source for a tableview. The statement looks like this:
SELECT defindex, AVG(price) FROM prices WHERE quality = 5 AND price_index != 0 GROUP BY defindex ORDER BY AVG(price) DESC
I was studying the SQLite.swift documentation to ind out how to do it properly, but I couldn't find a way. I could call prepare on the database and iterate through the Statement object, but that wouldn't be optimal performance wise.
Any help would be appreciated.
Most sequences in Swift can be unpacked into an array by simply wrapping the sequence itself in an array:
let stmt = db.prepare(
"SELECT defindex, AVG(price) FROM prices " +
"WHERE quality = 5 AND price_index != 0 " +
"GROUP BY defindex " +
"ORDER BY AVG(price) DESC"
)
let rows = Array(stmt)
Building a data source from this should be relatively straightforward at this point.
If you use the type-safe API, it would look like this:
let query = prices.select(defindex, average(price))
.filter(quality == 5 && price_index != 0)
.group(defindex)
.order(average(price).desc)
let rows = Array(query)

Simple Procedure raise ORA-06502

There's the simplified version of my code who keep raise me ORA-06502:
declare
p_filter varchar2(300) := '2012';
p_value varchar2(300) := '12345.000';
w_new_value number(13,3) := null ;
w_count number(4) := null ;
BEGIN
SELECT count(*)
INTO w_count
FROM dual
where p_filter = p_filter;
--- more filters
if w_count != 0 then
w_new_value := p_value / w_count;
else
w_new_value := p_value;
end if;
-- do something
end;
/
Someone can give me a help?
DataBase Details
nls_language = italian
nls_territory = italy
nls_currency = �
nls_iso_currency = italy
nls_numeric_characters = ,.
nls_calendar = gregorian
nls_date_format = dd-mon-rr
nls_date_language = italian
nls_characterset = we8iso8859p15
nls_sort = west_european
nls_time_format = hh24:mi:ssxff
nls_timestamp_format = dd-mon-rr hh24:mi:ssxff
nls_time_tz_format = hh24:mi:ssxff tzr
nls_timestamp_tz_format = dd-mon-rr hh24:mi:ssxff tzr
nls_dual_currency = �
nls_nchar_characterset = al16utf16
nls_comp = binary
nls_length_semantics = byte
nls_nchar_conv_excp = false
First, this is always going return a value of 1.
SELECT count(*)
INTO w_count
FROM dual
It doesn't matter what the qualifier is.
Lastly, I just ran your simplified code example in Oracle 11R2 and it didn't throw an exception.
I added the following statement in place of your "do something" comment:
dbms_output.put_line('w_new_value: ' || w_new_value || '. w_count: ' || w_count);
The result was:
w_new_value: 12345. w_count: 1
So, I think you've simplified your example into oblivion. You need to provide something that actually shows the error.
Good luck.
I found myself the ansewer and i think is useful for other know.
The real problem of the script for my DB is the language.
The italian "version" of Oracle accept , instead of the . for translate the VARCHAR2 into NUMBER unlike the most of other country.
For make the code running well the solution is
w_new_value := replace(p_value,'.',',') / w_count;
This trick finally allows the DB use my VARCHAR2 param like a NUMBER

Further problems with counting occurrences of strings in an Array

I am copying a question and answer from elsewhere as it partly goes into what I need but not completely.
In ASP classic, is there a way to count the number of times a string appears in an array of strings and output them based on string and occurrence count?
For example if I have an array which contains the following :
hello
happy
hello
hello
testing
hello
test
happy
The output would be:
hello 4
happy 2
test 1
testing 1
The answer that was given was this:
I'm assuming the language is VBScript (since that's what most people use with classic ASP).
You can use a Dictionary object to keep track of the individual counts:
Function CountValues(pArray)
Dim i, item
Dim dictCounts
Set dictCounts = Server.CreateObject("Scripting.Dictionary")
For i = LBound(pArray) To UBound(pArray)
item = pArray(i)
If Not dictCounts.Exists(item) Then
dictCounts.Add item, 0
End If
dictCounts.Item(item) = dictCounts.Item(item) + 1
Next
Set CountValues = dictCounts
End Function
This is great but I can't work out how to grab the top 2 most used words, display them and be able to put them in their own variable for use elsewhere.
Can anyone help with this?
You can loop through the dictionary object using this method. Inside that loop keep track of the top two keys and their counts in either a new array or two new variables.
You can't sort a Dictionary object in VBScript, so you have to use something else.
My advice is using a disconnected Recordset object to hold the items and their occurrences. Such object natively support sorting and it's pretty easy to use. To achieve this have such function instead:
Function CountValues_Recordset(pArray)
Dim i, item
Dim oRS
Const adVarChar = 200
Const adInteger = 3
Set oRS = CreateObject("ADODB.Recordset")
oRS.Fields.Append "Item", adVarChar, 255
oRS.Fields.Append "Occurrences", adInteger, 255
oRS.Open
For i = LBound(pArray) To UBound(pArray)
item = pArray(i)
oRS.Filter = "Item='" & Replace(item, "'", "''") & "'"
If (oRS.EOF) Then
oRS.AddNew
oRS.Fields("Item").Value = item
oRS.Fields("Occurrences").Value = 1
Else
oRS.Fields("Occurrences").Value = oRS.Fields("Occurrences").Value + 1
End If
oRS.Update
oRS.Filter = ""
Next
oRS.Sort = "Occurrences DESC"
oRS.MoveFirst
Set CountValues_Recordset = oRS
End Function
And using it to achieve the output you want:
Dim myArray, oRS
myArray = Array("happy", "hello", "hello", "testing", "hello", "test", "hello", "happy")
Set oRS = CountValues_Recordset(myArray)
Do Until oRS.EOF
Response.Write(oRS("item") & " " & oRS("Occurrences") & "<br />")
oRS.MoveNext
Loop
oRS.Close
Set oRS = Nothing
Don't forget to close and dispose the recordset after using it.

VB.Net: Convert SQL (Pseudocode) to Dymanic LINQ Query from List<Custom> Parameters

Okay guys. I'm a noob. I know (some) programming, a little SQL, and scant LINQ to SQL.
GOAL: Bind nested ListViews to LINQ generated iQueryable of anonymous type. I want to use LINQ because you can use GroupBy and bind the nested ListView to the 'it' keyword.
SETUP: I have groups of sets of conditions. Each set of conditions is stored in the BillingCodes table. Each group of BillingCodes is stored in the BillingGroups table.
I have a custom object that stores the ID, Name, and NumCodes for each BillingGroup that the user has chosen.
I have a collection of these objects called GroupsList that has a list of the groups that the user has chosen.
Problem 1: I can iterate through GroupsList and grab all the IDs. How do I translate the SQL 'WHERE ID IN(a string of comma delineated IDs)' for LINQ to SQL? Is that the best way to do that?
Problem 2: Once I have the list of BillingGroups I need to iterate through each group. For each group, I need to iterate through the BillingCodes. For each BillingCode I need to generate a WHERE clause that has all of the conditions in the BillingCode. I propose something like so:
for each BillingGroup in BillingGroups
for each BillingCode in BillingGroup.BillingCodes
where1 = "..."
next
next
Problem 3: Here's the part where I don't have a clue. I need to dynamically create a query in LINQ to SQL. Keep in mind that I don't know how many groups there'll be or how many codes are in each group.
There are 2 tables:
**transactions**
transaction_id
patient_id
svc_date
code
charge
description
**v_patients**
first_name
last_name
patient_id
date_of_birth
insname
active
provider_name
I imagine a query that looks something like this:
[Group1] Select MAX(svc_date), patient_id, transaction_id
From (
Select transaction_id, patient_id, svc_date
From transactions join v_patients on patient_id
[Code1] Where code=”” and description contains “” and insurance contains “” and charge >= PriceFloor and charge <= PriceCeiling
UNION
Select transaction_id, patient_id, svc_date
From transactions join v_patients on patient_id
[Code2]Where code=”” and description contains “” and insurance contains “” and charge >= PriceFloor and charge <= PriceCeiling
)
Group By patient_id
UNION
[Group2] Select MAX(svc_date), patient_id, transaction_id
From (
Select transaction_id, patient_id, svc_date
From transactions join v_patients on patient_id
[Code1]Where code=”” and description contains “” and insurance contains “” and charge >= PriceFloor and charge <= PriceCeiling
UNION
Select transaction_id, patient_id, svc_date
From transactions join v_patients on patient_id
[Code2]Where code=”” and description contains “” and insurance contains “” and charge >= PriceFloor and charge <= PriceCeiling
)
Group By patient_id
Problem 4: Lastly, I want to wrap that query in one that groups by patient_id. Something that end in Select as New With {key, it as transactions, num as count()}
I have pieced together this knowledge with endless reading and searches. I'll continue to look for answers, but any help would be GREATLY appreciated.
Thanks.
EDIT - ANSWER:
Here's the code that ended up working for me:
Dim chosenIDs() As Short = (From p In GroupsList _
Select p.ID).ToArray()
GroupMatchListView.DataSource = Nothing
If chosenIDs.Length > 0 Then
Dim db As New AudioRxInternalDataContext
Dim vf As New VersaformDataContext
Dim chosenGroups() As BillingGroup = (db.BillingGroups.Where(Function(m) chosenIDs.Contains(m.ID))).ToArray()
Dim wholeResults As List(Of transaction) = Nothing
For Each chosenGroup As BillingGroup In chosenGroups
Dim groupResults As List(Of transaction) = Nothing
For Each chosenCode As BillingCode In chosenGroup.BillingCodes
Dim codePredicate = PredicateBuilder.True(Of transaction)()
codePredicate = codePredicate.And(Function(i) i.code.Equals(chosenCode.Code))
If Not chosenCode.Description Is Nothing Then codePredicate = codePredicate.And(Function(i) i.description.ToUpper().Contains(chosenCode.Description.ToUpper()))
If Not chosenCode.Insurance Is Nothing Then codePredicate = codePredicate.And(Function(i) i.v_patient.insname.ToUpper().Contains(chosenCode.Insurance.ToUpper()))
If Not chosenCode.PriceFloor Is Nothing Then codePredicate = codePredicate.And(Function(i) i.charge >= chosenCode.PriceFloor)
If Not chosenCode.PriceCeiling Is Nothing Then codePredicate = codePredicate.And(Function(i) i.charge <= chosenCode.PriceCeiling)
If groupResults Is Nothing Then
groupResults = vf.transactions.Where(codePredicate).ToList()
Else
groupResults.AddRange(vf.transactions.Where(codePredicate).ToList())
End If
Next
groupResults = groupResults.GroupBy(Function(r) r.patient_id).SelectMany(Function(g) g.Where(Function(r) r.svc_date = g.Max(Function(a) a.svc_date))).ToList()
If wholeResults Is Nothing Then
wholeResults = groupResults
Else
wholeResults.AddRange(groupResults)
End If
Next
Dim conditionsPredicate = PredicateBuilder.True(Of transaction)()
conditionsPredicate = conditionsPredicate.And(Function(i) i.v_patient.active = "Y")
conditionsPredicate = conditionsPredicate.And(Function(i) i.svc_date >= StartDateBox.Text)
conditionsPredicate = conditionsPredicate.And(Function(i) i.svc_date <= EndDateBox.Text)
If Not OfficeDropDownList.SelectedValue = "Both" Then conditionsPredicate = conditionsPredicate.And( _
Function(i) (If(i.v_patient.provider_name, "").ToUpper().Contains(OfficeDropDownList.SelectedValue.ToUpper())))
wholeResults = wholeResults.Where(conditionsPredicate.Compile()).ToList()
Dim goliath = From f In wholeResults _
Group f By f.v_patient Into Group _
Order By v_patient.last_name, v_patient.first_name, v_patient.date_of_birth _
Select New With {.PatientID = v_patient.patient_id, .LastName = v_patient.last_name, .FirstName = v_patient.first_name, _
.DOB = v_patient.date_of_birth, .Ins = v_patient.insname, .MatchCount = Group.Count(), .Matches = Group}
GroupMatchListView.DataSource = goliath
theMatchesLabel.Text = goliath.Count()
Else
theMatchesLabel.Text = "0"
End If
Don't ask me why I used goliath for the final variable. I created that bit of code late at night, and the previous attempt had been named david.
Thanks for everyone's suggestions!
EDIT : Shame on me : I didn't use VB, but c#. But I hope some answers should help a bit...
Problem 1 : grab a list or array of int (or string) named Ids, for example and use
.Where(m => Ids.Contains(m.Id)
If the list of Ids comes from Database, I think you have to make 2 queries...
Problem 2 : not really clear with the infos you give, but the use of
SelectMany(x => blabla)
somewhere should do the trick (but once again hard to say like so)
Problem 3 : here also not really clear : what's the interest of you union in same groups ? If the difference are only codes, why not use the system of Problem 1 ?
To build "dynamic queries", I can just say that IQueryable can be build "on demand", like classic code, for example
var query = blabla;
if (searchCriterion.Name != null)
query = query.Where(m => m.Name == searchCriterion.Name);
Problem 4 : I use a "ToDictionary()" extension, as you seem to need a KeyValuePair, but they are of course other ways.
GroupBy(m => m.TransactionId).ToDictionary(m => m.Key, m => m.Count)
But... if you could be a little bit more concrete, maybe ;)
Edit Problem 4 :
didn't read well, rather something like that
GroupBy(m => m.TransactionId).Select(g => new {
patientId = g.Key,
transaction = g.SelectMany(p => p.Transactions),
num = g.Count());

dataset and non existing rows

Please help out newbie.
I am reading mothly sales statistics for last two years from stored procedure, display it on asp.net site and it works just fine.
Problem is with products that are not sold often I need to figure out which months do not have any sales. In that case I need to put zero in table cell and move to next row in dataset.
For...Each does not do the trick in case where there isn't data for every month.
Question is, how to move to next sqlrow and how to test when all rows heve been read?
sqlSelect = "EXECUTE dealer_sales_statistics #productID = '" & strProdID.Value & "'"
Dim sqlConn As New SqlConnection(sqlConnStr)
Dim sqlRow As DataRow
sqlConn.Open()
Dim sqlAdapt As New SqlDataAdapter(sqlSelect,sqlConn)
Dim sqlDataSet As New DataSet()
sqlAdapt.Fill(sqlDataSet, "sales_statistics")
Do Until sqlRow.EOF
If intCounter < 12 Then
' arrMonth contains last 24 months, e.g. "12_2009" to "1_2008"'
' stored procedure can return values for any month between that range'
' amount of returned values (DataSet sqlRows) can vary from 0 to 24'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_1 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_1 = arrSumma_1 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_1 &= "<td class='cell'>0</td>"
End If
Else
'using intCouter and same code to separate sales in 12 month periods'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_2 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_2 = arrSumma_2 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_2 &= "<td>0</td>"
End If
End If
intCounter = intCounter + 1
Loop
I think that you are focusing on the wrong area by trying to do this in your code. I can think of a likely solution there but it is really messy. Instead, focus on making sure that the sets returned by the stored proc are complete so you can iterate them without worry about missing months. That is, the stored procedure is probably returning sets made up only of months where there were sales (e.g. due to an inner join) - and you need to change this so it returns all months.
So, instead of posting the VB code, I'd suggest that you post the stored proc to get help in resolving the issue.
As a general guideline, I'd approach this by creating a dummy table with the months of the year listed (along with their month numbers to perform the join). Then, fold that table in with the query using a left outer join to ensure that all months are represented. Also, when selecting the final sales figures, make sure that there are no null values (for months where there were no sales) by using an "IsNull(Val, 0) as Val" to substitute a zero.
Again, this is just general guidance, we'd need to see the actual sproc to really help.
Here is how I did solve this with SQL. I create dynamically temp table that holds last 24 months and another temp table with sales data 0 to 24 months. Maybe this will help somebody with similar problem. (code below is in sql server as stored procedure). Thank you for help Mark!
DECLARE #strTemp_months TABLE
(
sorting INT,
months INT,
years INT
)
DECLARE #mnth INT
SET #mnth = 0
WHILE (#mnth < 24)
BEGIN
INSERT #strTemp_months
SELECT CASE WHEN YEAR(GETDATE()) = YEAR(DATEADD( m , -#mnth , GETDATE())) THEN 1 ELSE 2 END AS sorting,
MONTH(DATEADD( m , -#mnth , GETDATE())), YEAR(DATEADD( m , -#mnth , GETDATE()))
SET #mnth = #mnth + 1
END
DECLARE #productID VARCHAR(12)
SET #productID = '1234567890'
DECLARE #strTemp_statistics TABLE
(
sorting INT,
months INT,
years INT,
productno VARCHAR(35),
salesqty DECIMAL(9,2)
)
INSERT #strTemp_statistics
SELECT CASE WHEN YEAR(transaction_date) = YEAR(GETDATE()) THEN 1 ELSE 2 END AS sorting,
MONTH(transaction_date) AS months, YEAR(transaction_date) AS years, product_number AS productno,
SUM(qty) AS salesqty
FROM sales_events
WHERE product_number = #productID
-- including all transactions from last 24 full months until today
AND transaction_date >= CAST(YEAR(DATEADD( m , -23 , GETDATE())) AS CHAR(4)) + '-' + CAST(MONTH(DATEADD( m , -23 , GETDATE())) AS VARCHAR(2)) + '-01'
GROUP BY MONTH(transaction_date), YEAR(transaction_date), product_number
SELECT m.sorting, m.months, m.years, COALESCE(productno, 'No Sales') AS productno, COALESCE(kpl, 0) AS salesqty
FROM #strTemp_months m LEFT OUTER JOIN #strTemp_statistics s
ON m.months = s.months AND m.years = s.years
ORDER BY 1, 2 DESC

Resources