ADODB.recordset connection not finding data - sql-server

I'm trying to retrieve specific data from SQL Server via ADODB.recordset connection, and certain records are not found for some reason, (while they are found when running the same query in SQL SERVER itself) and I can't figure out why.
My code:
Dim adset As Object, cnct As Object
Set adset = CreateObject("ADODB.recordset")
Set cnct = thiscnn
adset.Open "SELECT prt FROM prt WHERE prt = 2204018", cnct, 1
While in other module:
Public thiscnn As Object
...
Set thiscnn = CreateObject("ADODB.Connection")
thiscnn.Open "DSN=...;Description=...;DATABASE=...;;UID=...;PWD=..."
thiscnn.CommandTimeout = 30
I'm talking here about code working well for years.
The thought that such a simple and useful part of my program which is in vast use, doesn't fetch the data loyally, drives me nuts. I need to give my customers proper, real and true information. I can't have such code sometimes returning and sometimes not returning data.
I know other ways of fetching data, but it is very important for me to understand why this simple way doesn't work all of a sudden in specific cases. It's so peculiar.
I would be greatful for any clue as to what could be happening in my case.
I tried fetching the same query in SQL SERVER itself, and worked fine - but that's the problem, it works in SQL SERVER, but not in VBA with this code above.
I tried looking for differences between records getting fetched and those which don't, and didn't see any special difference.
I tried refetching the same record a few times, but the fetching status for those specific records which don't get fetched (via ADODB.connection / recordset) stays the same.
I tried fetching the same records using the BETWEEN operator rather than the equals (=) operator, yet with no success.
I tried ordering by id column (prt), perhaps the problem is from certain id and up.
I tried ordering by the timestamp column thinking maybe from certain timestamp we have a problem - but that didn't give me any clue.
I thought to cast timestamp to date, but I found out that it's only rowversion and doesn't preserve a date or time.
I tried ordering by row size using this, thinking maybe certain ro size is to much for some odd reason, but that didn't help either.

Just a guess, but can it be some dirty data that isn't persisted?
Try selecting with nolock:
SELECT prt FROM prt (NOLOCK) WHERE prt = 2204018
And perhaps change cursor type:
adset.Open "SELECT prt FROM prt WHERE prt = 2204018", cnct, 2
Or you are looking into wrong database. Usually these errors are something stupid :D

Related

"Invalid attempt to read when no data is present.", but "hasrows" is true

I wonder if anyone can shed any light on why I'm not getting data in this piece of code:
Private Sub RecoverUnsentOrderToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RecoverUnsentOrderToolStripMenuItem.Click
' Find orders for this branch with status = "1" - created but not acked from the server
Dim myxDBReader As SqlDataReader
Dim myxDBcmd As SqlCommand
Dim query As String
query = "select * from orders where branch = #branch and status = 1;"
myxDBCmd = New SqlCommand(query, myDBCnn)
myxDBcmd.Parameters.Add("#branch", SqlDbType.VarChar).Value = BranchCode
myxDBReader = myDBCmd.ExecuteReader
If myxDBReader.HasRows Then
Do While myxDBReader.Read
Stop
Loop
End If
BranchCode and my database connection are public variables. When I run this code, it gets as far as the "Stop" and stops, but when I try to use the results, for example in the immediate window by trying to ? myxdbreader(0).tostring, I'm getting "Invalid attempt to read when no data is present" exceptions. When I hover over myxdbreader to view the results, I get a list of the rows, but cannot see the data in them.
This is inside a reasonably large (for me, but not massive) VB application which executes all manner of queries and retrieves data without any problems. The code is copied and pasted from another section of the code where it works quite well. The database connection is a single one, opened when the application is started and passed around as required. Another part of the application writes into this "orders" table without any problem, using the same database connection.
I have another toolstripmenu function which is identical in every respect except the query, in this case it is simply
select * from linkstatus where id=1
and that has the same issue - stops inside the do while dbreader.read loop so it has obviously found a row, but will not allow me to access the data in the way that I normally do.
Because it gets to the "Stop", I know that HasRows is indeed true, and it appears to have read the first row. The only thing I can see that is different is that this code is run from a Menu that I added to the form today, whereas all the rest of the code is run from a variety of buttons on the main form.
Everywhere I've looked up that error message, it appears to be because people have not executed "Read".
This is vb.net from Visual Studio 2019, accessing SQL Server 2018.
You are using a Public connection object, which as the comments state isn't the way to go. But most importantly, note that only one SqlDataReader can be associated with one SqlConnection, compounding the issue of a single shared connection.
SqlDataReader.Read Method
Only one SqlDataReader per associated SqlConnection may be open at a time, and any attempt to open another will fail until the first one is closed. Similarly, while the SqlDataReader is being used, the associated SqlConnection is busy serving it until you call Close.
It's probable that you already have an open Reader, and that is why you are seeing results for another data set.
As noted in the comments by #jmcilhenny, #Jeroen Mostert and #Jimi the root cause of this was using a single public database connection. I have changed the code now so that it connects and disconnects to the database each time I need to access it using a private connection, and it now works correctly.

Stored procedure returns null to VB

I have a problem I just can't figure out, my Google-fu has failed me too, so I signed in just to get some expert help.
The problem is complex, with a few red herrings, for what I can figure out, plus as we deal with sensitive information, I can't give the exact code. Please bear with me, I'll try to be as precise and descriptive as I can.
Short version of the problem: I have a stored procedure (on a SQL Server 2008 R2 server) which returns one row when executed directly on the server. When the same stored procedure is executed via VB.net code (Visual Studio 2013 environment), one of the column's value is changed to DBNull.
Long version with code example:
Here is a cleaned up version of the stored procedure
Create PROCEDURE MySP
#inputParms [five of them]
AS
BEGIN
DECLARE #UserCanRun int
DECLARE #ThisApp INT
EXEC #UserCanRun = dbname.dbo.CheckUserCanRun #InputParm1, #ThisApp --security check
IF #UserCanRun = 0 --Means no problem, can continue
BEGIN
DECLARE #ExtraInfo varchar(100)
SELECT #ExtraInfo = typeID + ' - ' + typeName
FROM [three joined tables]
WHERE [conditions using some #InputParm4]
SELECT DISTINCT
col1, ..., [colN-1], #ExtraInfo as colN, [colN+1], ..., colM
FROM
[8 joined tables and subqueries]
WHERE
[more conditions with #InputParms2 to 5]
END
END
GO
This stored procedure runs just fine when executed directly on the database; it returns one row, with all the (available) info having their value set correctly. (You'll notice colN; it's the one giving me trouble; co-workers assure me the fact it is using value from a variable shouldn't be a problem.)
Now, when I call it from VB.net code:
dim rTable As DataTable
dim myCmd = New SqlCommand(SPName, myConnectionString)
dim parameter As SQLParameter
dim rValue As Integer = 0
[set SQL parameters]
If not myCmd is Nothing Then
If myCmd.Connection.State = connectionstate.Closed Then
[bit of security check code]
command.Connection.Open()
End If
Dim myAdapter As New SqlDataAdapter
myAdapter.SelectCommand = myCmd
myAdapter.Fill(rTable) 'problem!
ReturnValue = CInt(myCmd.Parameters(RETURN_VALUE_FIELD).Value)
End If
[more code]
If I do a quick watch on the rTable after the myAdapter.Fill command has executed, the value rTable.Rows(0).Item([M]) returns DBNull, while it was not DBNull when fetched directly in the SP, using exactly the same parameters.
I don't have a data set for that table; you can see it uses a generic DataTable (the code is adapted from an application specific object to simplify all stored procedure calls, so I can't create a dataset and use it at this level either). So I don't think.
I have checked for the validity of the value; I have even checked the Unicode value of each character of the #ExtraInfo value to make sure there was not some control character that would have been put in accidentally. It matched the value shown, so no bad character messing up the value.
This is used in a few places in the code, but seems to fail for one specific set of data on our production environment; I haven't been able to reproduce the problem on our development environment. So I can't just go in and play with it.
Would anyone know what else could cause a column's value to go from a non-null, non-empty varchar to DBNull by going through SqlDataAdapter.Fill()? I can't step into that call, so I am blind.
(Also worth of note, maybe: I have been using VB.net for only about two years, debugging existing systems, so I may be missing something obvious; although my more experienced colleagues don't understand it either.)
Edited to add the final word on the problem: I never found the solution, except good old Murphy's Law. After letting this aside for a while, I asked a co-worker to take a look. As I was showing him, the problem didn't occur. Another test later confirmed it was behaving normally. I can't promise anyone Murphy's law is a sure solution, but I can only suggest there was some transient error that disappeared on its own. If it happens to you, good luck, and please let us know if you find a solution, or even a way to diagnose it. Thank you!
Your stored procedure needs SET NOCOUNT ON at the beginning

Unable to query nVarChar(Max) field in Access 2010

have used Stack Overflow as a resource hundreds of times, but my first time posting a question for some help!
I've got a table in SQL Server 2005 which contains 4 nVarChar(Max) fields.
I'm trying to pull out the data from an Access (2010) VBA Module using ADO 2.8
I'm connecting using SQL driver SQLNCLI10
(I can't use a linked table, as the 'table' I will ultimately be querying is a Table-Valued Function)
When I then print / use the recordset, the data is getting jumbled and concatenated with other fields in the same record - with a bunch of obscure characters thrown in.
The VBA: (various other methods were tried with the same result)
Sub TestWithoutCasting()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim i As Integer
cn.Open "Data Source=ART;DataTypeCompatibility=80;MARS Connection=True;"
Set rs = cn.Execute("SELECT * FROM JobDetail WHERE JobID = 2558 ORDER BY SeqNo ASC")
Do While Not rs.EOF
For i = 1 To rs.Fields.Count
Debug.Print rs.Fields(i).Name & ": " & rs.Fields(i).Value
Next i
rs.MoveNext
Loop
End Sub
Example Output:
SeqNo: 1
CommandID: 2
Parameter1: 2 Daily Report é [& some other chars not showing on here]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
Expected Output:
SeqNo: 1
CommandID: 2
Parameter1: SELECT Day_Number ,Day_Text ,Channel_Group_ID [...etc]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
So, it's grabbing bits of data from other fields instead of the correct data (in this case, it's an SQL statement)
I then tried casting the nvarchar(max) fields as text at source
View Created:
CREATE VIEW TestWithCast
AS
SELECT jd.JobID, jd.SeqNo, jd.CommandID
,cast(jd.Parameter1 as text) as Parameter1
,cast(jd.Parameter2 as text) as Parameter2
,cast(jd.Parameter3 as text) as Parameter3
,cast(jd.Parameter4 as text) as Parameter4
,jd.[Description]
,jd.Active
FROM JobDetail jd
Now, I initially had some luck here - using the same code as above does bring back data - but when I use this code in my main code (which jumps in & out of other procedures); as soon as I've queried the first result of the recordset, it appears to wipe the rest of the records / fields, setting them to Null. I also tried setting the value of each field to a variable whilst the rest of the vba runs before getting the next record - but this doesn't help either.
It almost feels like I need to dump the recordset into a local Access table, and query from there - which is a bazaar workaround for what is already a workaround (by casting as text).
I there something I'm completely missing here, or do I indeed need to cast as text and load to a local table?
Thanks for any help - it's driving me mad!
ps. Hope I've given the right level of detail / info - please let me know if I missed anything key.
EDIT:
Yikes, I think I've done it / found the issue...
I changed the driver to SQLSRV32 (v6.01) - and seems to work fine directly against the text casted field.
So... why would it work with an older driver but not the newer 'recommended' (by various sources I read) as the one to use.
And... will there be a significant drawback in using this over the native client?
EDIT 2:
Ok, I've tried a few drivers on a few machines, in each case with both the TEXT CASTING and Directly to VARCHAR MAX..
[On my windows 7 machine w/ SQLSMS 2008]
SQL Native Client 10.0 - Neither method works reliably with this driver
SQL Server 6.01 - BOTH methods appear to work reliably - further testing needed though
[On our production server w/ SQLS 2005]
SQL Native Client (v2005.90) - Does not work at all with varchar(max), but DOES work with text casting
SQL Server (v2008.86) - BOTH methods appear to work reliably - further testing needed though
This should make deployment interesting!
It's not a real answer, because I did not test it, but ... You are using a "DataTypeCompatibility=80" parameter in your connection. As far as I know, DataTypeCompatibility=80 refers to SQL Server 2000, where the nvarchar(max) field type was still not implemented.
I had the same problem, solved it by converting the field to an nvarchar(1000). Would be an easy, compatible solution for your problem if 1000 chars is enough.

Triple Inner join with over 10.000 rows and asp-calculations stalls application

My ASP Classic application are fetching over 10.000 rows via a triple inner join.
After this, it is going through each line, calculating some stuff and putting data in a multidimensional array with 17 coloumns.
All this are being fetched through a jQuery AJAX when pushing the search button.
Logically this takes a while, 5+ minutes actually.
It all works fine. BUT... While doing this, the whole system freezes, not only for the user doing the calculations, but also for all the other users using the system in other ways.
How can I optimize this?!
A small relevant code snippet:
dim userArray()
sql = "select first.*, second.fullname, second.info, third.inputs from first inner join second on first.userid = second.id inner join third on first.info = third.id where convert(varchar, first.period, 112) between '20020115' and '20120115' order by second.fullname, first.userid"
set rs = conn.execute(sql)
if rs.eof then
response.write("Nothing were found in the period")
else
counter = 0
redim userArray(17, 1000)
do until rs.eof
response.flush
counter = counter + 1
' A LOT of calculations and putting in array...
rs.movenext
loop
for i = 1 to counter
' Doing all the response.writes to the user...
next
end if
Lets analyse this whilst also bearing mind the SQL has an ORDER BY Clause:-
do until rs.eof
response.flush
counter = counter + 1
' A LOT of calculations and putting in array...
rs.movenext
loop
Note the Response.Flush, first thing I would do is get rid of that. You will probably need to increase the ASP Response Buffering Limit (in IIS manager). Flush is sending the generated content so far to the client, it waits for the client to acknowledge receipt of all the packets sent before it completes. That is where I'm gonna guess 90% of the 5+ minutes is being spent.
Now "A LOT calculations". VBScript is not know for its peformance. This code may well take some time. In some cases some calculations can be done much better by SQL than in script so that is one option. Another would be to build some COM compiled component to do complex work (although some accounting needs to made for marshalling which can wipe out benefits). However it may be unavoidable that you need to do these calcs in VBScript.
Now rs.movenext. This loop means you hold the connection and rowset open for pretty much all the time the processing is required. That is while the servers is sending bytes over the network to the client and while VBScript is crunching numbers. A much better approach would be suck up all the rowset quickly and disconnect from the DB, then crunch numbers and finally dump the buffer to the client.
Consider using a disconnected recordset (you specify a client side static cursor) or even the simple GetRows method of the recordset object that dumps the whole rowset into a 2-dimensional array. This will mean that you maintain locks on the various tables for the smallest time possible.
I see you already use response.flush() to flush data to the browser intermittedly during the process, but if you're using AJAX, the call must first complete before your AJAX callback function is called, so I think response.flush is not going to be of any use there.
You might try calling the AJAX url directly, and put in a response.write() in the loop to see what happens (and what the speed is)
To get even more information, you could add a timer before the query, after the query and inside the loop and response.write the time that has passed since starting the script. This will give you a very good idea where the delay is happening: http://www.codefixer.com/codesnippets/vbscript_timer_function.asp
You say the machine freezes, is that the client PC with the browser, or the server where IIS runs? If a bucketload of data is being sent I have seen browsers hang and not update until it's done.
Try to add WITH NOLOCK after each table name in your query to select without locking the database for writing. This might give you some data that was overwritten during the execution of your query, but that's usually not a problem.
Also, indexes. Try changing the clustered index on the fields you use in your WHERE statement, or add some regular indexes if you need your clustered index. Optimizing your indexes will speed things up considerably if you haven't done so.
HTH,
Erik
I'd refactor that code like this:
sql = "select first.*, second.fullname, second.info, third.inputs from first inner join second on first.userid = second.id inner join third on first.info = third.id where convert(varchar, first.period, 112) between '20020115' and '20120115' order by second.fullname, first.userid"
Set rs = conn.Execute(sql)
If NOT rs.EOF Then
aRecords = rs.GetRows() ' retrieve your records and assign them to an array '
End If
rs.Close ' record set is now released, so it shouldn't lock up your database anymore
If IsArray(aRecords) Then ' just a small sanity check '
iCount = UBound(aRecords, 2)
For iCounter = 0 To iCount
' Your calculations and your Response.Writes '
Next
Erase aRecords
Else
' no result found '
End If
Update
You can assign the records to variables in your For loop, e.g.
id = aRecords(0, iCounter)
Then you only need to refer to id when ever you need it. You're right though in that if what you're selecting is dynamic (i.e. column's positions can shift), then this approach can produce problems for when you're trying to assign records to variables further along the line. To assign fullname from your second table, for instance, you'd have to know how many columns are being retrieved from first.*.

VB.NET - What is wrong with my SQLinsert? Access database

I have a database with a bunch of stuff in it, and right now I'm reading in data, doing some processing on it, and then sticking it in a new database. My code generates this string:
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES (HELLO,123)"
Then it's used this way:
Dim InsertCmd = New System.Data.OleDb.OleDbCommand(query_string, connection)
InsertCmd.ExecuteNonQuery()
The associated database (OLEdb connection) exists and opens fine, with all the tables and columns it's trying to work with already existing. The error message I get is "No value given for one or more required parameters"
Am I missing something? Did I spell something wrong? I don't have a ton of experience with database work, but I've never had this trouble inserting before.
I believe the query should be
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES ('HELLO',123)"
Also, it may happen that the table has more than 2 columns that are NOT NUll and the values to them are required.
Consider parameterizing the query string. There are a couple of reasons for this. First, you can pass in the values without having to worry about whether or not you need single quotes. Second, you prevent SQL injection.
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES (#ExactPhrase,#OrgId)"
You then create parametes based on the parameter names in the string. Unless, of course, your query string is always the same values, but that sounds a bit too hardcoded to be good.

Resources