syntax error in ASP Classic SQL Query when using conditional IF - asp-classic

I am trying to sort out different elements in a ASP Classic SQL Query using IF and THEN, and have tried many different ways to get it to work .. the "problem child" is this:
StatsSQL = "SELECT COUNT(v.col) as num_not_null, COUNT(v.col) * 1.0 / COUNT(*) * 100 as percent_not_null, COUNT(*) as toltalColsNeedsFilled FROM EFP_EmploymentUser t CROSS APPLY (VALUES (t.ITAdvicedFirst),(t.ITAdvicedSecond),(t.ITDepartmentDone),(t.CFOAdvicedFirst),(t.CFOInfoProvided),(t.CFOAdvicedSecond),(t.CFODone),(t.EconomyAdviced),(t.EconomyDone)," & IF objFlowNotDone("Academy") = "yes" THEN Response.Write("(t.AcademyAdviced),(t.AcademyDone),") END IF IF objFlowNotDone("Publicator") = "yes" THEN Response.Write("(t.PublicatorAdviced),(t.PublicatorDone),") END IF & "(t.PortraitAdviced),(t.PortraitDone)) v(col) WHERE ID = '19';"
The Error I get:
Microsoft VBScript compilation error '800a03ea'
Syntax error
/flow.asp, line 271
Can anyone please help me in the right direction to solve this?
Best Regards
Stig :-)

You shouldn't be using Response.Write unless you actually want the contents of the Response.Write statement to be displayed in your output, and you really need to start a new line for your conditional statements. Here's what you appear to be trying to do. I'm assuming that your SQL is correct and the only issue here is with your VBScript.
StatsSQL = "SELECT COUNT(v.col) as num_not_null, COUNT(v.col) * 1.0 / COUNT(*) * 100 as percent_not_null, COUNT(*) as toltalColsNeedsFilled FROM EFP_EmploymentUser t CROSS APPLY (VALUES (t.ITAdvicedFirst),(t.ITAdvicedSecond),(t.ITDepartmentDone),(t.CFOAdvicedFirst),(t.CFOInfoProvided),(t.CFOAdvicedSecond),(t.CFODone),(t.EconomyAdviced),(t.EconomyDone)"
If objFlowNotDone("Academy") = "yes" THEN
StatsSQL = StatsSQL & ",(t.AcademyAdviced),(t.AcademyDone)"
End If
If objFlowNotDone("Publicator") = "yes" THEN
StatsSQL = StatsSQL & ",(t.PublicatorAdviced),(t.PublicatorDone)"
End If
StatsSQL = StatsSQL & ",(t.PortraitAdviced),(t.PortraitDone)) v(col) WHERE ID = '19';"

Related

Code won't execute after recordset returns using if-statement

Can anyone help me explain the following problem
In a SQL-query I get info about the occurrence of an id from another table.
Everything works here and I show a crucial snippet below
set rsRecordCount = Cn.execute(mysql_query)
dim cnt
cnt = rsRecordCount("TotalRecords")
response.write("cnt " & cnt & " id = " & id & "<br>")
rsRecordCount.Close
set rsRecordCount = nothing
So - via response.write I get info about the occurrence. The problem arises
when I want to test the cnt variable
if cnt = 0 then
response.write(".....<br>")
end if
just a simple test, the code stops execute here and I wonder why? I first thought cnt might work like a pointer pointing to a recordset and when setting it to null it would be error or undefined behaviour? But I've tried to comment out .Close and = nothing. The problem is still there.
Its really the cnt that is the prolem, If I use antoher variable - the code works again
if blablabla = 0 then
response.write("it works now<br>")
end if
How do I get around this/what did I miss
thanks!!!
I first thought cnt might work like a pointer pointing to a recordset
You were right in this assumption, but not 100% accurate. The default property of Recordset object returns a Field object, i.e. after this line:
cnt = rsRecordCount("TotalRecords")
The variable cnt is actually a Field object. Now you ask, why this line "works"?
response.write("cnt " & cnt & " id = " & id & "<br>")
That's because the Field object returns its Value property when being treated as a String.
However, when trying to compare to integer, VBScript fails to find any proper conversion and chokes.
There are two common ways to solve such a thing:
Convert the value to integer using CInt or CLng:
cnt = CLng(rsRecordCount("TotalRecords"))
Like the Response.Write above, this one first converts to String, then to Long. Profit.
Take the actual value, not the Field object:
cnt = rsRecordCount.Fields("TotalRecords").Value
Personally, I think the second way is more elegant and readable. Both ways work. Good luck!

Use a SQL Server stored procedure with unlimited number of multiple criteria instead of a view

I have a page where users select multiple search criteria to retrieve data from a SQL Server 2014 view. The view is grabbing data from a table on a linked server (I am not able to put the view directly on that server, and the table I am reading from has over 800 million rows so copying that data onto the local server isn't going to happen).
Of course, I can't index the view either (on linked server) so I'm trying to find a way to stop the timeouts from happening when the query is run. Is it possible to do something like this in a stored procedure?
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, COUNT(CASE WHEN fruitType = 'apple' THEN 1 ELSE NULL END) AS apple
, COUNT(CASE WHEN fruitType = 'banana' THEN 1 ELSE NULL END) AS banana
FROM
view_fruitReport
WHERE
(orchard = #orchard) and
and here's where it gets wonky. Users select the orchard from a dropdown (not a combobox because we use IE11 and ajaxtoolkit combo box still doesn't work there) so only one selection possible but.
They are able to add criteria to listboxes. Unlimited criteria. And they don't need to select any of the criteria, they can just search by orchard.
So the rest of the WHERE clause is built based on what they have added to the listboxes.
Like this:
' check if items selected in both listboxes'
If trees_Listbox.Items.Count > 0 Then
If fruitminListBox.Items.Count > 0 Then
'cycle through items in fruitnum listbox to create an "in" clause for sql query'
For Each item As ListItem In trees_Listbox.Items
whereString += String.Join(",", item) + ", "
Next
whereString = Left(whereString, Len(whereString) - 2) + ")"
selectQry += "(" + wherecls + whereString + ")"
whereFNcls = "(fruitNumber between "
For Each itemFNmin As ListItem In fruitminListBox.Items
'create a "between" clause for the min and max FN values entered by user.'
whereOEcls += itemFNmin.Value + " and " + fruitmaxListBox.Items(i).ToString + ") or (fruitNumber between " '(fruitnumber between number and number) or '
i += 1
Next
'trim off the last text portion of the whereOEcls'
whereOEcls = Left(whereOEcls, Len(whereFNcls) - 25)
selectQry += " and (" + whereFNcls + ") GROUP BY trees ORDER BY trees"
fruityData.SelectCommand = selectQry
WeeklyGridView.Visible = True
Else
'see if FN is empty but trees is selected'
For Each item As ListItem In trees_Listbox.Items
whereString += String.Join(",", item) + ", "
Next
whereString = Left(whereString, Len(whereString) - 2)
selectQry += wherecls + whereString + ") GROUP BY trees ORDER BY trees"
fruityData.SelectCommand = selectQry
WeeklyGridView.Visible = True
End If
Else
Essentially ending up with a where clause that could look like this:
WHERE (orchard = #orchard)
and trees in (100,200,300,400)
and fruitnumber between (itemFNmin.Value and itemFNmax.Value)
or fruitnumber between (itemFNmin.Value and itemFNmax.Value)
etc etc etc
Which works except it makes things very ugly and I am certain is a poor way of doing this.
I have no clue if/how I can make these lists of variables pass to a stored procedure as multiple arrays or tables etc.
Probably anything is better than having them tied to a view, whose linked server table isn't even an indexed table (not my fault haha)
For your first question: You can return the Count of each fruit type, but it will have performance implications as it requires a subquery for each one. This also requires that you hard code each possible fruit type in the query. I assume that the fruit types can change or have other types added to them, so this isn't the most desirable in terms of maintenance either. You can't dynamically add columns to a query unless you build SQL in your proc and make use of sp_executesql, which is more convoluted than doing in line SQL in your .Net code.
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, CASE
WHEN fruitType = 'apple' THEN (SELECT COUNT(fruitType) FROM view_fruitReport WHERE fruitType = 'apple') ELSE NULL
END AS [apple]
, CASE
WHEN fruitType = 'banana' THEN (SELECT COUNT(fruitType) FROM view_fruitReport WHERE fruitType = 'banana') ELSE NULL
END AS [banana]
FROM
view_fruitReport
WHERE
(orchard = #orchard)
For your second question, you can pass in lists/tables into a stored procedure. One method is to pass some sort of delimited string and parse it using T-SQL. I recommend a different approach, however, which is
Table Value Parameters. This is a parameter that acts as a table that you can join with in your stored procedure.
Here is an example for implementing a Table Value Parameter for the Trees column.
You will first need to declare a SQL Type:
CREATE TYPE [dbo].[Trees] AS TABLE (Trees INT)
Then you can reference it in your stored procedure as a parameter that acts as a table. Note that you can't use WITH(NOLOCK) with these and must specify READONLY in the paramter:
CREATE PROCEDURE [dbo].[up_getOrchardInfo]
(
#Trees As [dbo].[Trees] READONLY
, #Orchard INT
)
AS
BEGIN
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, COUNT(CASE WHEN fruitType = 'apple' THEN 1 ELSE NULL END) AS apple
, COUNT(CASE WHEN fruitType = 'banana' THEN 1 ELSE NULL END) AS banana
FROM
view_fruitReport AS F
INNER JOIN #Trees AS T
ON F.Trees = T.Trees
WHERE
(orchard = #orchard)
END
GO
The above example will filter by the Trees passed in. Note that if want to return everything for the Orchard if #Trees is Null or the count is 0 you will need to include that conditional logic in your stored procedure.
IF (#Trees IS NULL OR (SELECT COUNT(1) FROM #Trees = 0))
BEGIN
--No Join to #Trees
END
ELSE
BEGIN
--Query from above.
END
Finally, on the .Net side you will need to pass in a DataTable object as a Parameter on the SqlCommand with the Type of Structured:
Dim sqlCommand As New SqlCommand("up_getOrchardInfo", sqlConnection.SqlConnection)
sqlCommand.CommandType = CommandType.StoredProcedure
Dim sqlTreesParameter As New SqlParameter("#Trees", SqlDbType.Structured)
sqlOrchardParameter.Direction = ParameterDirection.Input
Dim tblExample As New DataTable
tblExample.Columns.Add("Trees", New Integer().GetType())
Dim drExample As DataRow = tblExample.NewRow()
drExample.Item("Trees") = 100
tblExample.Rows.Add(drExample)
'Adjust if Orchard is a VarChar/String'
Dim sqlOrchardParameter As New SqlParameter("#Orchard", SqlDbType.Int)
sqlOrchardParameter.Direction = ParameterDirection.Input
sqlOrchardParameter.Value = intYourOrchardValue
sqlCommand.Parameters.Add(sqlTreesParameter)
sqlCommand.Parameters.Add(sqlOrchardParameter)
'Execute Dataset
All of this said, you may want to consider making multiple stored procedures. One stored procedure could be optimized for returning everything when only an Orchard is passed and another for when Trees are also passed. This depends on how many parameters you're dealing with.
Maybe there's a UX answer. When you know it's not going to come back for a long time give the user a heads up and confirm if they want to wait or not. (With check box on the confirmation that says, "don't show me this again.")
But don't re-event the godawful estimated file transfer time from Windows Explorer.

Control Source property: Dcount: Unable to specify Multiple Condition

I have two commands in Microsoft Access 2010 that works fine individually:
=DCount("*","Order","DATE = #" & [Forms]![formOrder]![DATE] & "#")
=DCount("*","Order", "STATUS = 'ST." & [Forms]![formOrder]![StatusType] & "'")
However, when combining them it doesn't work:
=DCount("*","Order","DATE = #" & [Forms]![formOrder]![DATE] & "#" AND "STATUS = 'ST." & [Forms]![formOrder]![StatusType] & "'" )
Any explanation and a possible workaround would be much appreciated?
The third argument to domain aggregate functions like DCount() is a string that is essentially the WHERE clause of a SQL statment without the WHERE keyword. In trying to combine your criteria you are attempting to AND two strings together, which is not valid. That is, your criteria parameter is
"Condition1" AND "Condition2"
which won't work. Instead, you need to put the AND within the string itself
"Condition1 AND Condition2"
In other words, instead of
"Field1 = value1" AND "Field2 = value2"
you need to use
"Field1 = value1 AND Field2 = value2"
You've got too many double quotes:
=DCount("*","Order","DATE = #" & [Forms]![formOrder]![DATE] & "# AND STATUS = 'ST." & [Forms]![formOrder]![StatusType] & "'" )
I removed the one after the second hashmark, and one in front of the word Status.
Essentially, the third part of the DCount function is an SQL WHERE clause without the word "WHERE", and you must follow the same syntax and structure rules as with standard SQL.

Return an integer if nothing is found (TSQL ASP.NET VB)

I need to return a value (0) if nothing is found in an SQL call.
Here is what i have (edited/simplified to make more sense out of context), this is baing called from the codebehind.
sql1 = "INSERT INTO [Xtr_MenuItems]([menu_order])
values(1 + (select max(menu_order) from [Xtr_MenuItems]))
SO into database i insert the max number found in [menu_order] + 1. this works fine, assuming something is found.
However, if (select max(menu_order) from [Xtr_MenuItems])) fails (nothing found) then i want to return 0 (as 1 + nothing = nothing, and sql explodes)
How can i do this? I have tried 'IF EXISTS', 'OUTPUT' in various ways but cant get it to work...
Try this:
sql1 = "INSERT INTO [Xtr_MenuItems]([menu_order])
values(1 + ISNULL((select max(menu_order) from [Xtr_MenuItems]),0))
I used ISNULL function where if the result of query is null returns 0
Instead of values, you could use select straight away:
insert Xtr_MenuItems
(menu_order)
select 1 + isnull(max(menu_order),0)
from Xtr_MenuItems
COALESCE is the standards compliant way to provides alternatives for null values:
sql1 = "INSERT INTO [Xtr_MenuItems]([menu_order])
values(1 + COALESCE((select max(menu_order) from [Xtr_MenuItems]),0))
It has similar syntax to the ISNULL function provided by Microsoft, but will work across multiple db platforms and can handle multiple fallbacks.
W3Schools privides a great introduction to null handling here.

ASP.NET query substring

I've got this field in my database year_start_1 and it is an integer field and an example of an ouput is 20100827 I'm trying to create a substring to create year, week, day and change the format to be 27/08/2010
Here's what i'm trying
Dim query as String = "Select * from openquery (devbook, 'SELECT cast(year_start_1 as varchar(8)) as year_start_1, DATENAME(DAY, substring(CAST(year_start_1 AS VARCHAR(8)),6,2) + DATENAME(MONTH, substring(CAST(year_start_1 AS VARCHAR(8)),4,2) + DATENAME(YEAR, substring(CAST(year_start_1 AS VARCHAR(8)),1,4))) FROM web_statements')"
It's just throwing up an error and I not sure why:
Server was unable to process request
I have tried using convert but it doesn't work.
Any ideas?
UPDATE
with Chris's suggestion
Dim query as String = "Select * from openquery (devbook, 'SELECT year_start_1, cast(year_start_1 as varchar(8)) as year_start_1, substring(CAST(year_start_1 AS VARCHAR(8)),7,2)+''/''+substring(CAST(year_start_1 AS VARCHAR(8)),5,2)+''/''+substring(CAST(year_start_1 AS VARCHAR(8)),1,4) FROM web_statements')"
Still getting the error
Thanks
UPDATE
Couldn't seem to get it to work within the query so had to do a work around in the ASP.Net code
'POINTS END DATE YEAR
Dim strPointsDateEndYear = Mid(drv.Row("year_end_1"), 3, 2)
Dim strPointsDateEndMonth = Mid(drv.Row("year_end_1"), 5,2)
Dim strPointsDateEndDay = Right(drv.Row("year_end_1"), 2)
Dim strPointsDateEnd As String = strPointsDateEndDay + "/" + strPointsDateEndMonth + "/" + strPointsDateEndYear
Thanks for the help though
Your first DATENAME doesn't seem to close all its brackets before the next DATENAME starts:
DATENAME(DAY, substring(CAST(year_start_1 AS VARCHAR(8)),6,2) + DATENAME[...]
Should I assume be:
DATENAME(DAY, substring(CAST(year_start_1 AS VARCHAR(8)),6,2)) + DATENAME[...]
Edit: Though having fixed that minor error (and converted it to debug) I'm getting errors about casting strings to dates. I'm not sure what the datename stuff is meant to do but how about this:
DECLARE #year_start_1 int
SET #year_start_1 = 20100827
SELECT cast(#year_start_1 as varchar(8)) as year_start_1,
substring(CAST(#year_start_1 AS VARCHAR(8)),7,2)+'/'+substring(CAST(#year_start_1 AS VARCHAR(8)),5,2)+'/'+substring(CAST(#year_start_1 AS VARCHAR(8)),1,4)
(converted to a non table based select for test/debug purposes)
So what you would want for your final line of sql would be (untested):
Select * from openquery (devbook, 'SELECT cast(year_start_1 as varchar(8)) as year_start_1, substring(CAST(year_start_1 AS VARCHAR(8)),7,2)+'/'+substring(CAST(year_start_1 AS VARCHAR(8)),5,2)+'/'+substring(CAST(year_start_1 AS VARCHAR(8)),1,4) FROM web_statements')
Second Edit for debug notes:
I thought it might also be worth suggesting how to debug this sort of problem. The error message suggested that the linked server you were sending the sub-statement to was unable to process the request. The first thing to try in this case would be to run the request directly on the server to see what happens. In this case in fact just parsing it on its own would have revealed the first errors I got.
Once you have your statement running form management studio or whatever directly on the server you can try converting it back into the "openquery" style statement and see if ti still works. Bascially break down your complicated scenario into lots of smaller bits to test each one individually.
select convert(varchar(10),convert(smalldatetime,'20100827'),103) will return 08/27/2010
so, your solution is:
select convert(varchar(10),convert(smalldatetime,convert(varchar,year_start_1)),103) will return 27/08/2010
You are using the datename function on strings, but it should be used on a datetime value. They are nested inside each other so you would end up with just the day, and besided it doesn't even do what you are looking for.
To get the format that you asked for, you can just use string operations to split it up in it's components and add slashes between them:
substring(CAST(year_start_1 AS VARCHAR(8)),7,2) + '/' +
substring(CAST(year_start_1 AS VARCHAR(8)),5,2) + '/' +
substring(CAST(year_start_1 AS VARCHAR(8)),1,4)

Resources