Stored procedure output parameters return empty - sql-server

In my SQL Server 2014 I have a Stored procedure that returns 2 values in 2 variables as output:
#TotalNoRatio
#TotalRatio
Here are the results after execution:
#TotalNoRatio #TotalRatio
34510793 31857292
Return Value 0
Now I want those 2 values to be display in a Label on my form.
Here is the code:
cmd2.CommandType = CommandType.StoredProcedure
cmd2.Parameters.Add("#TotalNoRatio", SqlDbType.Decimal)
cmd2.Parameters.Add("#TotalRatio", SqlDbType.Decimal)
cmd2.ExecuteNonQuery()
Me.LTotal1.Text = cmd2.Parameters("#TotalNoRatio").Value
Me.LTotal2.Text = cmd2.Parameters("#TotalRatio").Value
Everything runs fine without errors except that the results are empty.

You need to define direction as return something like this:
SqlParameter retval = sqlcomm.Parameters.Add("#TotalNoRatio", SqlDbType.Decimal);
retval.Direction = ParameterDirection.ReturnValue;

You will need to specify the direction of you parameters as ParameterDirection.Output. You will also need to declare your parameters on your procedure as OUTPUT.
I have put together a small example below.
This is my procedure:
CREATE PROCEDURE [dbo].[procedureName]
#TotalNoRatio DECIMAL(18,2) OUTPUT,
#TotalRatio DECIMAL(18,2) OUTPUT
AS
SET #TotalNoRatio = 2
SET #TotalRatio = 3
This is my VB.NET code:
Using con As New SqlConnection(conString),
cmd As New SqlCommand("procedureName", con) With {.CommandType = CommandType.StoredProcedure}
con.Open()
cmd.Parameters.Add(New SqlParameter("#TotalNoRatio", SqlDbType.Decimal) With {.Direction = ParameterDirection.Output})
cmd.Parameters.Add(New SqlParameter("#TotalRatio", SqlDbType.Decimal) With {.Direction = ParameterDirection.Output})
cmd.ExecuteNonQuery()
lTotal1.Text = "TotalNoRatio: " & cmd.Parameters("#TotalNoRatio").Value.ToString()
lTotal2.Text = "TotalRatio: " & cmd.Parameters("#TotalRatio").Value.ToString()
End Using
This is a screenshot of the output:
On a seperate note consider turning Option Strict On:
Restricts implicit data type conversions to only widening conversions, disallows late binding, and disallows implicit typing that results in an Object type.
cmd.Parameters("#TotalNoRatio").Value returns type Object. You should be appending .ToString() to it if you're assigning to Label.Text.
Also note that I have implemented Using. You may already have, it's difficult to tell but if you haven't it's worth doing:
Sometimes your code requires an unmanaged resource, such as a file handle, a COM wrapper, or a SQL connection. A Using block guarantees the disposal of one or more such resources when your code is finished with them. This makes them available for other code to use.

Related

Is conversion from string to decimal needed and if so how should it be done?

I am trying to write a code for correction of entries to my SQL Server database. I am a mechanical engineering student who has a programming class and I have never programmed before so I am not sure should I convert string to decimal and how. Last 2 rows contain 2 options I came up with. Second one is what I use for pure string, first one is a modification of formatting datetime.
This is my stored procedure:
ALTER PROCEDURE [dbo].[SP_RN_O_Ispravak]
#Br_RN_O bigint,
#Datum_O DateTime OUTPUT,
#Sifra_p int OUTPUT,
#Ime_P nvarchar (30) output,
#Prezime_P NVarChar(30) OUTPUT,
#Naziv_P nvarchar (50) output,
#Adresa_P nvarchar (50) OUTPUT,
#Telefon_P NVarChar(15) OUTPUT,
#Sifra_z int OUTPUT,
#Ime_Z nvarchar (30) output,
#Prezime_Z nvarchar (30) output,
#Sifra_kul nvarchar (3) OUTPUT,
#Naziv_Kul NVarChar(20) OUTPUT,
#Masa_O decimal (5,0) OUTPUT,
#Vlaga_O decimal (4,1) OUTPUT,
#Hek_Masa_O decimal (3,1) OUTPUT,
#Protein_O decimal (3,1) output,
#Cijena_O decimal (3,2) output
AS
SELECT #Br_RN_O=T_Otkup.Br_RN_O,
#Datum_O=T_Otkup.Datum_O,
#Sifra_p=T_Otkup.Sifra_p,
#Sifra_z=T_Otkup.Sifra_z,
#Sifra_kul=T_Otkup.Sifra_kul,
#Masa_O=T_Otkup.Masa_O,
#Vlaga_O=T_Otkup.Vlaga_O,
#Hek_Masa_O=T_Otkup.Hek_Masa_O,
#Protein_O=T_Otkup.Protein_O
FROM T_Otkup
WHERE (T_Otkup.Br_RN_O = #Br_RN_O)
SELECT #Prezime_P=Prezime_P
FROM T_Poljoprivrednik
WHERE Sifra_P=#Sifra_p
SELECT #Prezime_z=Prezime_Z
FROM T_Zaposlenik
WHERE Sifra_Z=#Sifra_z
SELECT #Naziv_kul=Naziv_Kul
FROM T_Kultura
WHERE Sifra_Kul=#Sifra_kul
RETURN
This procedure is supposed to pull the data from the database and place it in textboxes shown in the image.visual of whati'm trying to make
I managed to use the following code to convert decimal to string:
Dim cijenao As SqlParameter = New SqlParameter("#Cijena_O", Data.SqlDbType.Decimal, 3, 2)
cijenao.Direction = Data.ParameterDirection.Output
cijenao.Value = Cijena_O.Text
cmd.Parameters.Add(cijenao)
Masa_O.Text = Format(masao.Value, "#####").ToString
Vlaga_O.Text = Format(vlagao.Value, "###.#").ToString
Hek_Masa_O.Text = Format(hmasao.Value, "##.#").ToString
Protein_O.Text = Format(proto.Value, "##.#").ToString
However, it doesn't work for 2 decimal places like this:
Cijena_O.Text = Format(cijenao.Value, "#.##").ToString
I tried using the code posted by Mary, but it get the following message:
System.Data.SqlClient.SqlException: 'Procedure or function SP_RN_O_Ispravak has too many arguments specified.'
I've cleaned up this Sub, to properly scope the connection, and make sure the Connection and Command objects get disposed (via Using). It is always best to explicitly handle the data type conversions, such as using .ToString() on the .Value property of the parameters. Note I also parse the Long before assigning it to the input parameter (although you should Google the .TryParse() method and use that).
Protected Sub ISPRAVAK_NALOGA()
Using conn As New SqlConnection(<your connection string here>)
Using cmd As New SqlCommand("SP_RN_O_Ispravak", conn) With {.CommandType = CommandType.StoredProcedure}
With cmd
.Parameters.Add("#Br_RN_O", SqlDbType.BigInt).Value = Long.Parse(Br_RN_O.Text)
.Parameters.Add("#Masa_O", Data.SqlDbType.Decimal, 5, 0)
.Parameters("#Masa_O").Direction = ParameterDirection.Output
.Parameters.Add("#Vlaga_O", Data.SqlDbType.Decimal, 4, 1)
.Parameters("#Vlaga_O").Direction = ParameterDirection.Output
conn.Open()
.ExecuteNonQuery()
Masa_O.Text = .Parameters("#Masa_O").Value.ToString
Vlaga_O.Text = .Parameters("#Vlaga_O").Value.ToString
End With
End Using
End Using
End Sub
As to your code...
Check the available overloads for the Constructors for the Parameter class. https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlparameter?view=netframework-4.8#constructors There are none that match your code. Dim masao As SqlParameter = New SqlParameter("#Masa_O", Data.SqlDbType.Decimal, 5, 0) The final parameter of the constructor with 4 parameters is a string holding the name of the source column.
A Bigint in Sql Server maps to and Int64 in .net. (A Long in vb.net) This is a good reference for mapping datatypes from Sql Server to .net. https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings.
Now my code...
Declare the variable outside the using blocks.
The TryParse is a very clever method that not only test a string but fills the variable with the converted string when it succeeds. Return is functionly equivalent, in this case, to the vb.net specific Exit Sub . Return is often used in other languages (think C#).
Keep your databse objects local so you can control their closing and disposing. A Using block will do this for you even if there is an error. You don't need to create new variable for the parameters. They can be referred to by name in the Parameters collection. Set the values of maso an vlaga inside the Using block before the command is disposed.
After the database objects are duly discharges, we can set the values in the User Interface. Reguarding the .ToString method; N0 (the 0 is a zero) will give you a string containing the number with no decimal protion. The N stands for Number and the 0 is the number of decimal places. It adds commas to make the number easier to read and it will round as appropriate. See https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings for more details.
Ideally the database code and the UI code would be separated but that is for another day.
Protected Sub ISPRAVAK_NALOGA()
Dim maso As Decimal
Dim vlaga As Decimal
Dim InputNumber As Long
If Not Long.TryParse(Br_RN_O.Text, InputNumber) Then
MessageBox.Show("Please enter a valid number.")
Return
End If
Using conn As New SqlConnection("Your connection string")
Using cmd As New SqlCommand("SP_RN_O_Ispravak", conn)
cmd.CommandType = CommandType.StoredProcedure
With cmd.Parameters
.Add("#Br_RN_O", SqlDbType.BigInt).Value = InputNumber
.Add("#Masa_O", Data.SqlDbType.Decimal)
.Add("#Vlaga_O", Data.SqlDbType.Decimal)
End With
cmd.Parameters("#Vlaga_O").Direction = ParameterDirection.Output
cmd.Parameters("#Masa_O").Direction = Data.ParameterDirection.Output
conn.Open()
cmd.ExecuteNonQuery()
maso = CDec(cmd.Parameters("#Masa_O").Value)
vlaga = CDec(cmd.Parameters("#Vlaga_O").Value)
End Using
End Using
Masa_O.Text = maso.ToString("N0")
Vlaga_O.Text = vlaga.ToString
End Sub
I am a bit unsure of what you intent.
But if you want to know if you need to convert a number into a string before assigning the value to a textbox, then the answer is yes. You do need to convert it.
But there are a few things that you can do to display decimal values in a more readable way. For example, you can set the textbox customFormat to #########0.00 or ######,##0.00

LIKE statement using parametrized SQL in vbscript

I'm trying to create a LIKE SQL statement in vbscript using parametrized SQL.
The command text passed through is along the lines of
SET NOCOUNT ON;
DECLARE #pname as nvarchar(50);
SELECT #pname = ?;
SELECT *
FROM tblProject
WHERE projName LIKE #pname
And I'm concatenating % onto either side of the string that I'm passing through as pname. If I change the LIKE to an = and remove the %s then it works fine. And if I run the statement directly and replace #pname with '%searchterm%' it works fine. Any help would be much appreciated.
Edit: vbscript:
Set rsAnswers = Server.CreateObject("ADODB.Recordset")
Set cmd = server.createobject("ADODB.Command")
cmd.CommandText = mySQL
cmd.CommandType = 1
cmd.CommandTimeout = 900
cmd.ActiveConnection = svrPerformanceConnectionset prm = cmd.CreateParameter("#pname", 129, 1, 50, thisProjName)
cmd.Parameters.Append prm
rsAnswers.CursorLocation = 3
rsAnswers.Open cmd, , 0, 1
(thisProjName is getting a string from a form)
Thanks,
Tim
it is the expected behaviour because the parameter value is escaped to handle it as a value.
the solution is to move the concatenation of the % out of the parameter:
SET NOCOUNT ON;
SELECT *
FROM tblProject
WHERE projName LIKE '%' + #pname + '%';
notice the missing declaration & initialization of the parameter: inizialization and value assignement are performed by the VBScript engine and data access layer when creating the related objects in the script.
After trying many things, I found that the problem was the data type I was declaring my parameter as. I'm not sure why exactly but using adVarChar (200) rather than adChar (129) made the LIKE statement work.
Thanks to everybody who answered and commented!

Weird SQL Error (Bug)

So this is really weird.
I run a sql command from .net on sqlserver with a 'Select Count(*)' and get a response like "Needs attention CA" (which is in a varchar of one field of one record of the inner joined tables).
Huh? How can Count(*) return a string? 999 out of 1000 times this code executes correctly. Just sometimes on some clients servers it will throw a string of errors for an hour or so only to miraculously stop again.
This is my sqlcommand:
SELECT Count(*)
FROM patientsappointments
INNER JOIN appointmenttypes
ON patientsappointments.appointmenttypeid =
appointmenttypes.appointmenttypeid
WHERE ( ( patientsappointments.date > #WeekStartDate
AND patientsappointments.date < #WeekFinishDate )
AND ( patientsappointments.status = 'Pending' )
AND ( patientsappointments.doctorid = #DoctorID )
AND ( appointmenttypes.appointmentname <> 'Note' ) )
And these are the parameters:
#WeekStartDate = 24/06/2013 12:00:00 AM (DateTime)
#WeekFinishDate = 1/07/2013 12:00:00 AM (DateTime)
#DoctorID = 53630c67-3a5a-406f-901c-dbf6b6d1b20f (UniqueIdentifier)
I do a sqlcmd.executescalar to get the result. Any ideas?
The actual executed code is:
SyncLock lockRefresh
Dim WeekFulfilled, WeekPending As Integer
Using conSLDB As New SqlConnection(modLocalSettings.conSLDBConnectionString)
Dim mySQL As SqlCommand
mySQL = New SqlCommand("SELECT COUNT(*) FROM PatientsAppointments INNER JOIN AppointmentTypes ON PatientsAppointments.AppointmentTypeID = AppointmentTypes.AppointmentTypeID " & _
"WHERE ((PatientsAppointments.Date > #WeekStartDate AND PatientsAppointments.Date < #WeekFinishDate) AND (PatientsAppointments.Status = 'Pending') " & _
"AND (PatientsAppointments.DoctorID = #DoctorID) AND (AppointmentTypes.AppointmentName <> 'Note'))", conSLDB)
Try
mySQL.Parameters.Add("#WeekStartDate", SqlDbType.DateTime).Value = MonthCalendar1.SelectionStart.Date.AddDays(-MonthCalendar1.SelectionStart.Date.DayOfWeek).AddDays(1)
mySQL.Parameters.Add("#WeekFinishDate", SqlDbType.DateTime).Value = MonthCalendar1.SelectionStart.Date.AddDays(-MonthCalendar1.SelectionStart.Date.DayOfWeek).AddDays(8)
mySQL.Parameters.Add("#DoctorID", SqlDbType.UniqueIdentifier).Value = cboDoctors.SelectedValue
conSLDB.Open()
'got errors here like "Conversion from string "R2/3" to type 'Integer' is not valid." Weird.
'failing on deadlock - maybe due to simultaneous updating from udp event. Try adding random delay to refresh
WeekPending = mySQL.ExecuteScalar
Catch ex As Exception
ErrorSender.SendError("frmAppointmentBook - RefreshHeader 1", ex, New String() {String.Format("mySQL.commandtext: {0}", mySQL.CommandText), _
String.Format("mySQL.Parameters: {0}", clsErrorSender.ParamsListToString(mySQL.Parameters))})
End Try
Me.lblPendingWeek.Text = WeekPending
Try
mySQL.CommandText = "SELECT COUNT(*) FROM PatientsAppointments INNER JOIN AppointmentTypes ON PatientsAppointments.AppointmentTypeID = AppointmentTypes.AppointmentTypeID WHERE " & _
"(PatientsAppointments.Date > #WeekStartDate AND PatientsAppointments.Date < #WeekFinishDate) AND (PatientsAppointments.Status = 'Fulfilled') AND " & _
"(PatientsAppointments.DoctorID = #DoctorID) AND (AppointmentTypes.AppointmentName <> 'Note')"
'didn't get the error here... but just in case...
WeekFulfilled = mySQL.ExecuteScalar
Catch ex As Exception
ErrorSender.SendError("frmAppointmentBook - RefreshHeader 2", ex, New String() {String.Format("mySQL.commandtext: {0}", mySQL.CommandText)})
End Try
conSLDB.Close()
End Using
End SyncLock
The exact error message is:
System.InvalidCastException
Conversion from string "Needs Attention DC" to type 'Integer' is not valid.
Your problem has nothing to do with the COUNT(*) portion of your code. The problem is somewhere else in your query. What that particular error is telling you is that at some point you are comparing a character field (it probably usually contains numbers) to an integer field. One of the values of the character field happens to be "Needs Attention DC". If I had to guess it is probably either patientsappointments.appointmenttypeid or appointmenttypes.appointmenttypeid. Double check the datatype of each of those columns to make sure they are in fact INT. If they are both INT then start checking the other explicitly named columns in your query to see if you have any surprises.
You must have an error somewhere in your implementation...
Per the documentation, count always returns an int data type value.
Since this doesn't always happen, it must be a result of one of the paramenter values that is sent in. This is one of the lbuiggest problems with using dynamic SQL. What I would do is create the dymanic SQl and then store it in a database logging table with the date and time and user who executed it. Then when you get the exception, you can find the exact SQL code that was sent. Most likely you need more controls on the input variables to ensure the data placed in them is of the correct data type.
I am going to make another guess. I am guessing that this is a multi threading issue. You probably are sharing the connection between multiple threads. Once in a while the thread will get that man from somewhere else and execute it. Make sure that the connection variable is local, and only one thread can access it at a time.
As Martin points out, the following answer is wrong. I'm keeping this here to show that this is wrong.
From what everyone has already said, there is a type mismatch on your columns. Since your where clause appears to be fine, and your join is fine, it must be elsewhere. I would check to see if patientsappointments or appointmenttypes are views. Maybe the view has a join that's throwing the exception. Check the schema definition of all your joins/where's. Somewhere in there you're storing integers in a character field. It's fine for most rows, but one of them has your string.
If it's not in your views, it may be a trigger somewhere. The point is that somewhere there is a schema mismatch. Once you find your schema mismatch, you can find the row by querying for that string.

Result type from SQL Server stored procedure

Ok so I'm calling a stored procedure and I'm unsure of what type my VB.net code will be receiving.
Here's the stored procedure:
#IDNo varchar(Max)
AS
select a.IDNo,
p.EMAIL
from person p
left join customer a on p.person_id=a.person_id
where (a.IDNo=#IDNo)
Here's my VB so far;
resultSet = thisDataCxt.sp_GetEmail(IDNo).FirstOrDefault()
My question is, what type should resultSet be? Can I make it an Object? If so how do I access its IDNo and EMAIL properties.
Also need to bear in mind I'm using this to see if there are no results back (aka IDNo didn't have a match) so I need to test if resultSet is NULL.
Why cant you use Dataset & datatables and store the sp output in that.Then try to iterate the values through it if datatable has more than 0 rows.
It's going to be whatever you've mapped the result of the FunctionImport sp_getEmail to, probably a ComplexType. It's not going to be a set, per se, as you're taking the FirstOrDefault.
Incidentally, prefixing your SPs with sp_ is bad practice. http://sqlserverpedia.com/blog/sql-server-bloggers/stored-procedure-performance-using-%E2%80%9Csp_%E2%80%9D-prefix-%E2%80%93-myth-or-fact/
Check this sample code.
cmd.CommandText = "your proc name";
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param1 = cmd.Parameters.Add("#param1", SqlDbType.VarChar, 255);
param1.Direction = ParameterDirection.Input;
SqlParameter param2 = cmd.Parameters.Add("#param2", SqlDbType.Int, 8);
param2.Direction = ParameterDirection.OutPut;
cmd.ExecuteNonQuery();
Now you have your output value in #param2.

I need someone to explain this ASP function to me

I've got an ASP document that 5 years old. Actually I'm working with PHP but I must use ASP for a Windows Application. So I need someone to explain this function to me.
//DNS SETTINGS ARE INCLUDED ALREADY.
function Check_Is_Web_Locked()
dim cmdDB , Ret
OpenDatabase
Set cmdDB = Server.CreateObject("ADODB.Command")
With cmdDB
.ActiveConnection = DBCon
.CommandText = "TICT_CHECK_WEB_STATUS"
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("RETURN_VALUE", adInteger, adParamReturnValue, 0)
.Execute,,adExecuteNoRecords
Ret = Trim(.Parameters("RETURN_VALUE"))
End With
Set cmdDB = Nothing
CloseDatabase
Check_Is_Web_Locked = Ret
end function
What does this function do?
Is "TICT_CHECK_WEB_STATUS" a stored procedure?
If it's what are the columns that function looking for?
Yes, TICT_CHECK_WEB_STATUS is a stored procedure in the database. This SP returns a "signed integer" output parameter called RETURN_VALUE, whose value gets stored in the Ret variable when it is returned from the SP.
The Trim function should strip out any white-space from RETURN_VALUE, but since it is an integer there will never by any. Therefore it is simply converting the return value into a string.
Finally the function is returning the Ret string. This is done with the Check_Is_Web_Locked = Ret statement.
This looks like it's just a heartbeat of sorts to the database (i.e. the web page is saying "Hey Database, are you alive?" by calling TICT_CHECK_WEB_STATUS). And yes, TICT_CHECK_WEB_STATUS is a stored proc.
TICT_CHECK_WEB_STATUS is apparently a stored proc that returns an output parameter value called Return_Value. That value is stored in a variable called Ret.

Resources