Following this question, I need to add something like
SET LANGUAGE German;
before my SELECT query.
I am running the query in an ASP/VBScript web environment, which basically limits me, as far as I know, to a single query at a time.
If I run a query such as -
SET LANGUAGE German; SELECT.....
I get a no results message, because the 'data' returned is from the SET query and not the SELECT that follows it.
Is there anything that can be done to run the SET and the SELECT together in the ASP/VBScript environment?
UPDATE:
As per Lankymarts suggestion:
set rs = SERVER.CreateObject("ADODB.recordset")
rs.open sql, conn, 1, 2
Do While (rs.State = 0 Or rs Is Not Nothing) // also tried: Do While (rs.State = 0 AND rs Is Not Nothing)
Set rs = rs.NextRecordset
Loop
do while not rs.eof
response.write ...
UPDATE 2:
Now that the closed recordset issue is solved, I am still not getting rows from the main recordset.
This is my VBScript code, below.
There are definitely results (because 21/feb/16 - was on Sunday, and i have matching records for this) - but they are not being displayed. In fact even displaying via SSMS sometimes i dont get the results - maybe its getting all confused with the language changes?
sql = " SET LANGUAGE German; "
sql = sql & " SELECT [tblstudentrakazot].studentid, firstname, lastname, tblRakezetSchedule.* FROM tblRakezetSchedule"
sql = sql & " INNER join [tblstudentrakazot] on [tblstudentrakazot].scheduleID = tblRakezetSchedule.scheduleid "
sql = sql & " INNER join tblstudents on [tblstudentrakazot].studentid = tblstudents.studentid"
sql = sql & " WHERE CONVERT(int,scheduleday) = datepart(d,convert(datetime,'" & cleanSQL(planneddate) & "',103)) AND "
sql = sql & " tblRakezetSchedule.rakezetID = " & CleanSQL(x_rakezetID)
sql = sql & " ORDER BY replace(scheduletimefrom, ':', '')"
response.Write("### " & sql)
set rs = SERVER.CreateObject("ADODB.recordset")
rs.open sql, conn, 1, 2
Do While rs.State = 0 And Not rs Is Nothing
Set rs = rs.NextRecordset
loop
do while not rs.eof
' we now proceed to loop through the actual result recordset : studentid, firstname etc...
By the way - does the language remain in German after the query has run, or does it return to its default language?
I guess what i need here is a language setting whose default is dd/mm/yyyy (because of other legacy requirements in the system) and one that the DATEFIRST is Sunday (1).
ALSO:
I tried to make a stored procedure, as such:
ALTER PROCEDURE [dbo].[procListRakezetSlotsByDay] #planneddate nvarchar(10), #rakezetID int
AS
BEGIN
SET NOCOUNT ON;
SET LANGUAGE German;
SELECT [tblstudentrakazot].studentid, firstname, lastname, tblRakezetSchedule.* FROM tblRakezetSchedule
INNER join [tblstudentrakazot] on [tblstudentrakazot].scheduleID = tblRakezetSchedule.scheduleid
INNER join tblstudents on [tblstudentrakazot].studentid = tblstudents.studentid
WHERE CONVERT(int,scheduleday) = datepart(d,convert(datetime,#planneddate,103)) AND tblRakezetSchedule.rakezetID = #rakezetID
ORDER BY replace(scheduletimefrom, ':', '')
END
and then run it:
DECLARE #return_value int
EXEC #return_value = [dbo].[procListRakezetSlotsByDay]
#planneddate = N'28/2/2016',
#rakezetID = 182
SELECT 'Return Value' = #return_value
GO
and here too, it returns no results - even within SSMS...
I am VERY confused. thanks to all who have helped so far.
This is a misconception. Neither ASP/VBScript limits you, the limit is imposed by the provider ADODB uses to perform the command. In terms of SQL Server though there is no limit (I know of) when executing a command that contains multiple queries.
First
SET LANGUAGE German;
isn't really a returning query but the Provider will return it as a closed ADODB.Recordset object, which isn't ideal but there is a simple fix.
SET NOCOUNT ON;
Will inhibit DONE_IN_PROC messages from being sent to say the executing line was successful which is interpreted by ADODB as a closed ADODB.Recordset object.
Another way to deal with this but not as straight-forward as SET NOCOUNT ON is to use the NextRecordSet() method of the ADODB.Recordset object to step through the various resultsets until you find the actual query result.
Assuming rs is our starting ADODB.Recordset object
Do While (rs.State = adStateClosed And Not rs Is Nothing)
Set rs = rs.NextRecordset
Loop
will return the first ADODB.Recordset object that isn't in the closed state.
From MSDN - NextRecordset Method (ADO)
As long as there are additional results and the Recordset containing the compound statements is not disconnected or marshaled across process boundaries, the NextRecordset method will continue to return Recordset objects. If a row-returning command executes successfully but returns no records, the returned Recordset object will be open but empty. Test for this case by verifying that the BOF and EOF properties are both True. If a non–row-returning command executes successfully, the returned Recordset object will be closed, which you can verify by testing the State property on the Recordset. When there are no more results, recordset will be set to Nothing.
Related
I have a legacy classic ASP application running with SQL Server 2012 (also tested with 2016) that I am trying to switch over to using parameterized queries. All the site's queries run through a function which expects a sql statement as a string with parameters represented by question marks as well as an array of those parameters. The function currently filters the parameters to make them sql safe and puts them into the sql string before executing the statement.
Given this, I thought it would be pretty straightforward to switch this to parameterized queries. Initial testing looked good, and everything appeared to be working properly until I hit a sql statement with parameters in subqueries.
Here's a test sample of what works:
Const connectionString = "Provider=SQLNCLI11; DataTypeCompatibility=80; Server=********; Database=********; UID=*******; PWD=*******"
Dim sql, productId, parameters
sql = "SELECT SKU FROM Products WHERE ProductId = ?"
productId = 3
parameters = Array(productId)
Dim conn
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open connectionString
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = sql
cmd.Parameters.Refresh
Dim rs
Set rs = cmd.Execute(, parameters)
Response.Write("SKU: " & rs("SKU"))
No problem, this returns the SKU as expected. However, if I use a subquery:
Const connectionString = "Provider=SQLNCLI11; DataTypeCompatibility=80; Server=********; Database=********; UID=*******; PWD=*******"
Dim sql, productId, parameters
sql = "SELECT SKU FROM ( SELECT SKU FROM Products WHERE ProductId = ? ) AS P"
productId = 3
parameters = Array(productId)
Dim conn
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open connectionString
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = sql
cmd.Parameters.Refresh
Dim rs
Set rs = cmd.Execute(, parameters)
Response.Write("SKU: " & rs("SKU"))
It throws an error on the cmd.Parameters.Refresh line:
Microsoft VBScript runtime error '0x80004005'
Microsoft SQL Server Native Client 11.0
Syntax error, permission violation, or other nonspecific error
If I check cmd.Parameters.Count in the first sample, I correctly get 1. In the bad sample it throws the same error.
Is there any explanation as to why putting the parameter into a subquery causes problems with the parameter collection? I did try manually adding the parameter to the Parameters collection, and that works fine, but it means modifying hundreds of existing sql calls, so for the moment the cmd.Parameters.Refresh round-trip was worth the expense.
For anyone who might stumble across this, I finally figured out the issue thanks to a co-worker. It turns out there is nothing wrong with the code, but rather with the connection string. I somehow left it out of the sample code, but my connection strings included "DataTypeCompatability=80". If that is present, the code throws the error. However, if I remove it, the error no longer occurs and I get the results back as suspected.
My understanding from this KB article on using ADO with the native client is that DataTypeCompatability should be included to ensure newer data types work properly, but so far I have not found any issues with removing it.
You can give cmd.execute what you want, but I haven't used it in a long time.
cmd.execute("SELECT SKU FROM ( SELECT SKU FROM Products WHERE ProductId = ? ) AS P", Array(productId))
I'm struggling with an old and massive system developed on MS-Access 2007, which I'm in charge of its maintenance (including the implementation of changes).
The system interacts with a SQL-Server (2012).
Currently, I need to implement a change in which a new stored procedure needs to be invoked which returns a simple records set, each record being a string.
I tried to use for the invocation some code that already exists within the application (using ADO and copied into the form I'm working on) but nothing I tried works.
Here is the code I'm trying to use:
glblsqlstrToLabels = "EXEC p_Labels_Print 1 , 2878954 , 'OC9991' , '89029' , 4 , 1 , 'dummy'"
Though I'm using exactly the same connection string as it is being used all over the application, executing the above statement returns with an error message (something like ...not open...).
I'm starting to suspect that there is something wrong in the way I'm invoking the function (e.g. not defining any parameters for it and expecting a behavior similar to a select statement).
Any help will be highly appreciated.
EDIT:
Following are the pieces of code describing what I need to have working:
Dim RS As Recordset
' Connection string is: Provider=Microsoft.Access.OLEDB.10.0;Persist Security Info=True;Data Source=****;User ID=****;Password=****;Initial Catalog=***;Data Provider=SQLOLEDB.1
MyCommand = "EXEC p_Labels_Print 1 , 2878954 , 'OC9991' , '89029' , 4 , 1 , 'asdasd'"
RS.Open MyCommand, CurrentProject.Connection
Do Until RS.EOF
Print <record retrieved>
Loop
RS.Close
Set RS = Nothing
The error I get is: Error: Operation is not allowed when the object is closed.
You need to properly use ADO and commands, you can't just rs.Open a stored procedure.
Use the following Microsoft boilerplate code:
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = Conn1
Cmd1.CommandText = "sp_AdoTest"
Cmd1.CommandType = adCmdStoredProc
Cmd1.Parameters.Refresh
Cmd1.Parameters(1).Value = 10
Set Rs1 = Cmd1.Execute()
Where Conn1 is your ADODB connection, and fill in your parameters and stored procedure name.
Background: Work on frontend Ms-Access 2010 and backend SQL server 2008 Managment Studio
For executing stored procedures I have been using a pretty lengthy process as seen here: in VBA
Set Conn = New ADODB.connection
Conn.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;....."
Conn.Open
Set cmd = New ADODB.Command
cmd.ActiveConnection = Conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "upGetTestIdForAnalyte"
cmd.Parameters.Append cmd.CreateParameter("#WOID", adVarChar, adParamInput, 60, MySampleName)
cmd.Parameters.Append cmd.CreateParameter("#Analyte", adVarChar, adParamInput, 60, MyAnalyte)
cmd.Parameters.Append cmd.CreateParameter("#SampleID", adVarChar, adParamInput, 60, MyConcentration
cmd.Execute
Conn.Close
Someone told me there was a better way to execute a stored procedure and the correct way would be something like this: in VBA
strsql = "Exec upGetTestIdForAnalyte(WOID, Analyte, SampleID)"
test = ExecuteNonQuery(strsql)
But I got a lot of errors for this process and I looked up ExecuteNonQuery and it said it was only for VB (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx). One of the advantages of this process would be its conciseness and the fact that it connects automatically to the current database. I would have to change the connection string in my current sp process because it is set to link to a test database not the actual one. Another advantage would be that it returns a value when executed. I would like my current process to do that because I want to somehow verify it ran and other stored procedures I need to make, need to return the number of records affected.
My questions are: Is the second method a legitimate process and is that the correct syntax? Also is there any difference in what each process accomplishes? Also is there a way for the first process to return a value when executed? Thank you for your time.
UPDATE: This is my stored procedure I'm currently working on. My sp sees if a testID exists or not, I will continue with the program after calling the sp if variable ThisTestId >0 else I will raise an error testid not found
CREATE PROCEDURE upGetTestIDForAnalyte #WOID nvarchar(60), #SampleID nvarchar(60),#Analyte nvarchar(60), #Test var(20) output
AS
SELECT TestID = t1.TestID
FROM tblWOSampleTest t1
JOIN tblTest t2
ON t1.TestID=t2.TestID;
WHERE #WOID = t1.WOID AND #SampleID = t1.SampleID AND #Analyte = t2.Analyte
GO
The examples posted here have way too much code.
The original question is the poster has seen some examples where only one or two lines of code is required.
In fact this code works and passes a parameter to a store procedure.
With CurrentDb.QueryDefs("MyPass")
.SQL = "exec MySproc" & ProducutID
.Execute
End If
The above is only two lines of code.
The advantages of this approach?
Note how we did not have to mess (or pass) with a connection string.
Note how we did not have to declare any connection object in code.
Note how we did not have to store or have the user name or password anywhere in the code.
Note how we don’t have to create a query def object in code either.
In fact the whole thing works without declaring ANY variables.
And the above could have been written on two lines of code, but I decided to post a massive 4 lines of code for increased readability.
The advantages of this setup are many, but the main reason is such an approach is MOST simple and easy to maintain.
The other posted solutions here serve only to force one to write many lines of code – this simply increases development costs for their employers.
I'll stay with first process and add an output parameter like this:
ccmd.parameters.Append ccmd.CreateParameter("OutParam", adInteger, adParamOuput, , NULL) ' output parameter
You need to add this parameter in stored procedure as well like this:
#OutParam int OUTPUT
EDIT Added OUT parameter, changed to integer in VBA code. See how ##ROWCOUNT work
CREATE PROCEDURE upGetTestIDForAnalyte #WOID nvarchar(60), #SampleID nvarchar(60),#Analyte nvarchar(60), #RecordsAfected int OUT
AS
SELECT TestID = t1.TestID
FROM tblWOSampleTest t1
JOIN tblTest t2
ON t1.TestID=t2.TestID;
WHERE #WOID = t1.WOID AND #SampleID = t1.SampleID AND #Analyte = t2.Analyte
set #recordsAfected = ##ROWCOUNT
GO
From Microsoft help site:
Function ExecuteSPT (sqltext As String, connectstring As String)
' Purpose: Run a temporary pass-through query.
' Accepts: sqltext: SQL string to run.
' connectstring: Connection string, which must be at least
' "ODBC;".
' Returns: nothing.
Dim mydb As Database, myq As QueryDef
Set mydb = DBEngine.Workspaces(0).Databases(0)
' Create a temporary QueryDef object that is not saved.
Set myq = mydb.CreateQueryDef("")
' Set the ReturnsRecords property to False in order to use the
' Execute method.
myq.returnsrecords = False
myq.connect = connectstring
myq.sql = sqltext
myq.Execute
myq.Close
End Function
It can be easily altered to return as DAO.Recordset. DAO is still the "native" data access in MS Access.
I have the following UDF in excel which uses ADO to connect to my MSSQL server. There it should execute the scalar udf "D100601RVDATABearingAllow".
For some reason the parameters that I try to append are not send to the sql server. At the server only:
SELECT dbo.D100601RVDATABearingAllow
arrives.
MY EXCEL UDF:
Function RVDATA(Fastener) As Long
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim Cmd1 As ADODB.Command
Dim stSQL As String
Const stADO As String = "Provider=SQLOLEDB.1;Data ................"
'----------------------------------------------------------
Set cnt = New ADODB.Connection
With cnt
.ConnectionTimeout = 3
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 3
End With
'----------------------------------------------------------
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = cnt
Cmd1.CommandText = "dbo.D100601RVDATABearingAllow"
Cmd1.CommandType = adCmdStoredProc
'----------------------------------------------------------
Set Param1 = Cmd1.CreateParameter("Fastener", adInteger, adParamInput, 5)
Param1.Value = Fastener
Cmd1.Parameters.Append Param1
Set Param1 = Nothing
'----------------------------------------------------------
Set rst = Cmd1.Execute()
RVDATA = rst.Fields(0).Value
'----------------------------------------------------------
rst.Close
cnt.Close
Set rst = Nothing
Set cnt = Nothing
'----------------------------------------------------------
End Function
When I use adCmdStoredProc the whole thing fails and in the vba debugger the properties of the recordset has a lot of "Operation is not allowed when object is closed" (may sound a bit different, the message is translated)
When I don't use adCmdStoredProc I get the message that the variable Fastener was not provided.
I think that maybe something is wrong in the way I open the recordset.
In other treads I read about using the "SET NOCOUNT ON" option, but that did not work either.
Does anyone have a idea?
Regards Lumpi
Ran into this error as well (in my case I am using a Stored Procedure to retrieve some information). I had made some changes which caused the execution to malfunction.
The error disappeared when I put SET NOCOUNT ON as the first statement of the Stored Procedure.
You do not need to SELECT the server side function, just provide its name ("[tra-CAE400-1].dbo.D100601RVDATABearingAllow") in the .CommandText property.
Also you should set the .CommandType property to "stored-procedure" (property reference on w3schools.com).
Then adodb will know that you are talking about calling a function, and not trying to send a plain sql-command.
Chances are that it will then allow you to define the parameters on the command object.
But the parameters you define on the command object should correspond exactly (in name and type) to the ones that are defined as the arguments of the function in the sql server.
An example from microsoft.com on using the command-object with a stored procedure
ADO Reference on microsoft.com
Another possible cause of this is debug statements. I just spent far too long trying to work out why this wouldn't work for me, the Proc on the database worked fine, the data it was supposed to insert was inserted, the VBA code worked fine, but there was nothing in the recordset.
Final solution was to go through the procs that had been built and remove the PRINT statements.
To test if this is the problem, run your proc on SQL Server manually, then look at the messages tab of the results, if there's anything there other than "Command(s) completed successfully." you need to eliminate those messages. "SET NOCOUNT ON" will get rid of the row count messages, but there may be others.
I'm assuming that after 5 years the OP has solved this particular problem, so this is just for anyone like me that finds this while searching for the same problem.
I also ran into this with a stored procedure. Did you SET NOCOUNT = OFF; at the bottom of your code? That is what worked for me after lots of googling. Also, if you have any other code that runs, you have to wrap it in Nocount = on/off, INCLUDING insert and update statements. You would think that an insert statement wouldn't matter but wrapping the code that way is what kept me from committing suicide today.
In our shop we often use lines like this in our stored procedures to assist with debugging:
RAISERROR('Debug message here',0,1) WITH NOWAIT;
This also breaks opening a recordset in Excel vba. I believe the complete answer for this question is, in the stored procedure:
use SET ROWCOUNT OFF
remove all PRINT statements
remove all RAISEERROR statements used for debugging (ie severity of 0)
Using classic asp, I am trying to query a SQL Server database like so:
strSQL = "select column_1, column_2, column_3, column_4 from someview " &_
"where RecordNo=" & i
set rs=conn.Execute(strSQL)
if not rs.eof then
A = rs("column_1")
B = rs("column_2")
C = rs("column_3")
D = rs("column_4")
end if
Column_3 is an NText type, the other columns are varchar or int (sometimes there may be more than 4 columns returned) but the query only returns 1 record because of the where clause.
On the ASP page the results vary - sometimes A,B,D are populated, sometimes not - but all columns in the view contain data (when I query the SQL Server I see the expected results - all columns do contain data). If I remove column_3 which is NText from the strSQL everything works fine.
I've seen this behaviour on a couple other pages in the past. If I modify the ASP to get column_3 separately:
strSQL = "select column_3 from someview where RecordNo=" & i
The NText data is returned correctly.
Is there a maximum record length to a SQL Server recordset returned to classic ASP? Apart from splitting out the NTEXT into a separate query, is there anything else I can do?
EDIT: It just occured to me to try changing the connection string - inspired by this comment on a similar problem - the connection is via SQL Server ODBC Driver (Driver={SQL Server};).
I have had this problem. Microsoft acknowledge it somewhere on their website.
If you put the NText column last in the SELECT list, you will be able to access it ok.
However, your code cannot access any other columns after it has read the NText value. Once you move to the next row of the recordset you're OK again.
Best solution is to change your connection string though, and use something more modern. That solves the problem in the best way!
To avoid using the recordset, try this:
For 1 record returned:
arr = rs.Getrows
if IsArray(arr) then
A = arr(0)
B = arr(1)
C = arr(2)
D = arr(3)
end if
For more records:
aryList = rec.GetRows
iCount = Ubound(aryList,2)
For i = 0 to iCount
A = aryList(0,i)
B = aryList(1,i)
C = aryList(2,i)
D = aryList(3,i)
' Do something with A,B,C,D
Next
casting ntext to varchar will do the job.
You're mixing unicode data (the ntext column) with non-unicode (varchar). That may be the reason, since the ASP page has to decide which to use.
Try and use either one or the other (casting non-unicode data to unicode may be the better option).
One extra tip for those who are working with older code:
When a recordset's column value is blank using ADO/ASP and you have a single line of data, you can bypass this problem by using a parameterised Command statement and returning the string value into a variable:
Some hand-typed code hopefully explains what I mean:
' DB connection
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.CursorLocation = adUseClient
objCon.Open pubDbConnString
' statement construction
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = objCon
cmd.CommandText = "SELECT ?=T.Column1, ?=T.Column2 From Table T WHERE ID=?"
cmd.CommandType = adCmdText
' add parameters
cmd.Parameters.Append cmd.CreateParameter("#column1Data", adVarChar, adParamOutput, 8000)
cmd.Parameters.Append cmd.CreateParameter("#column2Data", adTinyInt, adParamOutput)
cmd.Parameters.Append cmd.CreateParameter("#id", adBigInt, adParamInput)
cmd.Parameters("#id").value = 1
set objRS = cmd.Execute
#column1Data will contain the large string. objRS will actually not have any records in it, so be mindful of this.
In theory, this should also work with named parameters with the same results, but I have not tested this.