Stored procedure returns null to VB - sql-server

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

Related

ADODB.recordset connection not finding data

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

OLE DB Source in Data Flow Issue with Variable

I’m experiencing a frustrating issue when trying to call a proc in an OLE DB source task. I’m using the SQL command from variable data access mode but I can see that it isn’t evaluating my variable correctly.
My variable (with ValidateAsExpression set to True) uses an expression to create a sql command like “EXEC ProcName ‘Param'” where the value of Param comes from a variable who’s value I set using an EXEC SQL task. Below is the expression:
“EXEC ProcName ” + “‘” + #[User::vDateThreshold] + “‘”
If I use a variable in my source that references a static value it works fine, but the issue seems to be when I use a variable which reference another variable in its expression.
Has anyone else come across this issue?
I’m using this method because I’ve had a similar issue when trying to use a parameter with the sql command data access method.
Thanks in advance
I’m using this method because I’ve had a similar issue when trying to use a parameter with the sql command data access method.
The right way to do that is by using SQL Command with parameters:
EXEC ProcName ?
And select #[User::vDateThreshold] as parameter.
Parameterized OLEDB source query
If it is not working then check your procedure code and make sure it generate a specific result set. If the result set is dynamic and columns are not fixed then you have to define it in the query using WITH RESULTSETS keyword.
https://www.mssqltips.com/sqlservertip/2356/overview-of-with-result-sets-feature-of-sql-server-2012/
From the name of #[User::vDateThreshold it seems like an SSIS datetime variable. Try setting this to a variable with an explicit cast and then executing the stored procedure with the variable. Make sure there that are single quotes (') within the CAST function as you would use if this was done in SSMS. When concatenating a datetime variable within a string variable in SSIS, the datetime variable must be converted to text, which is done with (DT_STR, length, codepage) in the sample expression below. I'm not sure what version you're using, but testing this out on SSDT for Visual 2017 worked fine for me. This will cover if you still want to hold the SQL in a variable, however the solution that #Hadi posted is a good option if you'd prefer to go that route.
"DECLARE #pDate DATETIME
SET #pDate = CAST('" + (DT_STR, 50, 1252)#[User::vDateThreshold] + "' AS DATETIME)
EXEC ProcName #pDate"
Thank you for the responses to my question.
I actually found the issue was with the ordering of my tasks in the package. When I looked closer at the values assigned to the relevant variables by using a break point on my exec SQL task I could see the wrong date was being passed to my proc. When I set the value of vDateThreshold at an earlier point the correct date value was assigned.
I think this was a case of looking at something for long enough that I was missing the obvious.

How to call SQL Server stored procedure with datetime parameter from VBA?

I have a SQL Server stored procedure that accepts two input parameters of datetime datatype. I want to call it from Excel VBA.
VBA doesn't have a datetime type so I've tried something like this:
Dim spCommand As ADODB.Command
Set spCommand = New ADODB.Command
spCommand.ActiveConnection = cnn
spCommand.CommandText = "myProcedureName"
spCommand.CommandType = adCmdStoredProc
spCommand.Parameters.Refresh
spCommand.Parameters(1).Value = "6/1/2016" 'DateValue("2016-06-01") + TimeValue("00:00:00")
spCommand.Parameters(2).Value = "6/1/2016" 'DateValue("2016-06-01") + TimeValue("23:59:59")
spCommand.Execute
But I get this error:
How can I solve it?
EDIT 1
After followeing what #Joe suggested, I opened my debugger and get this, but error is still there:
Use the DateSerial function:
spCommand.Parameters(1).Value = DateSerial(2016,1,6)
and, if you need it, optionally the TimeSerial function:
spCommand.Parameters(2).Value = DateSerial(2016,1,6) + TimeSerial(23,59,59)
VBA doesn't have a datetime type
Yes, it does:
Dim dtDateTime as Date
dtDateTime = DateSerial(2016,1,6) + TimeSerial(23,59,59)
UPDATE
Following your edit, I see that the Parameters collection has three items. The first is a return parameter, and the second and third are input parameters of type adDBTimeStamp.
Also the Command.NamedParameters property is false, so you probably can't use named parameters as you seem to be attempting to do in the screenshot accompanying your edit.
UPDATE 2
VBA collections are indexed starting at 1, not 0, so you should be specifying your parameters as:
spCommand.Parameters(2).Value = ...
spCommand.Parameters(3).Value = ...
I think the above is wrong. Standard VB/VBA collections are indexed, starting at 1. I believe the rationale, mistaken in my view, was that 1-based indexes are "more intuitive".
When ADO was developed, I believe Microsoft realized that 1-based collections had been a mistake, and decided to make ADO collections 0-based. This makes them inconsistent with standard collections, but "more intuitive" to, say, javascript developers. What an inconsistent mess.
If I'm right (and it's a long time since I used ADO, and I haven't tested this), then you need:
spCommand.Parameters(1).Value = ...
spCommand.Parameters(2).Value = ...
which should eliminate your 3265 error, but doesn't solve your original problem. Your debug trace appears to show that the parameters have been set to the expected values. I can only suggest you try to generate an MCVE, including a simplified version of the Stored Procedure in question.

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.

Understanding Classic ASP

I am looking through some old code and found a piece that i cant' seem to understand the point of....As far as i can tell, it's just a simple insert. but why did they do it this way? would it be okay to rewrite into an insert, or could i potentially break something?
please see below:
Set TextRS = Server.CreateObject("ADODB.RecordSet")
Set TextRS.ActiveConnection = Conn
TextRS.Source = "SELECT IDX,TIMESTAMP,CURRENTFLAG,TEXT FROM " & TextTable & " WHERE 1=2 FOR UPDATE"
TextRS.CursorLocation = 2
TextRS.CursorType = 3
TextRS.Open ,,,3
TextRS.AddNew
TextRS(0).Value = IDX
TextRS(1).Value = Timestamp
TextRS(2).Value = "Y"
TextRS(3).AppendChunk TextPiece
TextRS.Update
TextRS.Close
This part of the source confused me a bit.... where 1 = 2???
Apparently it had a purpose to ensure no match.
Anyway this style of programming is fairly old using ADO technology and people coming from DAO to ADO would often open up a cursor to iterate over the database this way... it does not follow modern best practices on how to do things, you can and should replace it with an insert statement!
It is possible that it was written pre jet4.0/access 2000 in which case it was an attempt to simulate a parameterized stored procedure. Though if the system is at all more modern than that I would strongly recommend using a stored procedure as it has multiple benefits. Cached Execution Plans, Parameters to reduce the chances of SQL injection
I actually used to write code very much like that 12 years ago or so :p Mostly because I just didn't know better, regardless of the tech in use.
Ah, good old classic ASP ;)
The 1 = 2 forces the sql to never return a match. It's basically a way of building up the command (?) so that you can then "conveniently" change the values and then an update will store it.
I've seen it done before, but never did it that way myself. As others have said, a simple paremetised INSERT statement will be better, IMO.
I would rewrite this using parameterized ADO query. The method being used has an unnecessary SELECT, which makes the INSERT slower.
That code seems a bit obscure, but all they are doing is creating an empty instance of a recordset row so the values can be set and the recordset resaved. This is bound to be much slower than doing a straight INSERT.
I would utilize an insert statement. The above mentioned code seems a little bit "lazy"... as in "let ADO do the work for me". However, there is nothing really wrong with it. The where 1=2 part was there to return an "empty table"... um... I mean recordset.

Resources