Return result set from SQL Server stored procedure to vb.net - sql-server

The following stored procedure works as I want in the Visual Studio designer. The result is a table containing all the race distances for the input #CourseName
ALTER PROCEDURE [dbo].[getCourseDistancesProc]
#CourseName nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT
RaceDistances.RaceDistance
FROM
RacingMaster
JOIN
RaceDistances ON RacingMaster.Dist_Of_Race_FK = RaceDistances.PKRaceDistancesId
JOIN
Courses ON RacingMaster.RM_Course_FK = Courses.PKCourseId
WHERE
CourseName = #CourseName
END
I want to call the stored procedure from a vb.net application. What data type do I declare as the output variable so that the full result set is returned to the calling app?

There was obviously more work to be done than I had realized, but just in case anyone else stumbles across this question the solution I finally adapted from elsewhere is:-
Dim myConn As SqlConnection
Dim myCmd As SqlCommand
Dim results As String
Dim ConnectionString As String
' Create the connection string.
ConnectionString = "Data Source=*********;" & _
"Initial Catalog=*******;" & _
"Integrated Security=SSPI;"
myConn = New SqlConnection(ConnectionString)
myConn.Open()
Dim InputName As String
InputName = TextBox1.Text
myCmd = New SqlCommand()
myCmd.CommandText = "getCourseDistancesProc"
myCmd.CommandType = CommandType.StoredProcedure
myCmd.Parameters.AddWithValue("#CourseName", Odbc.OdbcType.NVarChar).Value = InputName
myCmd.Connection = myConn
Dim myReader As SqlDataReader = myCmd.ExecuteReader()
If myReader.HasRows Then
Do While myReader.Read()
Dim var As String
var = myReader.GetString(0)
MsgBox(var)
Loop
Else
MsgBox("No rows found.")
End If
myReader.Close()
Obviously, the above is just to demonstrate that the requested data is indeed coming back from the database. But now I know that it is I can handle it in a more useful way.

Related

How to detect it was duplicate entry on VB.NET?

I have a problem on making avoid or detect duplicate data entry in VB.NET using SQL Server, and also pop up message when there is duplicate entry.
This is my add button code:
Dim sql = "INSERT INTO studentAttendence(studentID, date, time, subject, attendence) " &
"VALUES(#studentID, #date, #time, #subject, #attendence)" &
"WHERE NOT EXISTS(SELECT * FROM studentAttendence WHERE studentID = #studentID)"
Using cn As New SqlConnection("My sql Connection string")
Dim commands As SqlCommand = New SqlCommand(sql, cn)
commands.Parameters.Add("#studentID", SqlDbType.NVarChar).Value = TextBox1.Text
commands.Parameters.Add("#date", SqlDbType.NVarChar).Value = DateTimePicker1.Text
commands.Parameters.Add("#time", SqlDbType.NVarChar).Value = DateTimePicker2.Text
commands.Parameters.Add("#subject", SqlDbType.NVarChar).Value = ComboBox1.Text
commands.Parameters.Add("#attendence", SqlDbType.NVarChar).Value = ComboBox2.Text
commands.ExecuteNonQuery()
End Using
Or is there any other methods for making avoid duplicate entries. I just want to avoid same date and subject of a student to add. So I can only add subject on the next date if I already add subject on today's date of studentID.
You can use If Not Exists before the insert. This method only requires a single hit on the database. Add a comman at the end of the first line of the Using block, then delete Dim before commands. This will include commands in the Using block.
Private Function InsertAttendance() As Integer
Dim sql = "If Not Exists (Select 1 From studentAttendence
Where studnetID = #studentID And date = #date And subject = #subject)
INSERT INTO studentAttendence(studentID, date, time, subject, attendence)
VALUES(#studentID, #date, #time, #subject, #attendence);"
Dim NumRowsAffected As Integer
Using cn As New SqlConnection("My sql Connection string"),
commands As SqlCommand = New SqlCommand(sql, cn)
commands.Parameters.Add("#studentID", SqlDbType.NVarChar).Value = TextBox1.Text
commands.Parameters.Add("#date", SqlDbType.NVarChar).Value = DateTimePicker1.Text
commands.Parameters.Add("#time", SqlDbType.NVarChar).Value = DateTimePicker2.Text
commands.Parameters.Add("#subject", SqlDbType.NVarChar).Value = ComboBox1.Text
commands.Parameters.Add("#attendence", SqlDbType.NVarChar).Value = ComboBox2.Text
cn.Open()
NumRowsAffected = commands.ExecuteNonQuery()
End Using
Return NumRowsAffected
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If InsertAttendance() = 1 Then
MessageBox.Show("Success")
Else
MessageBox.Show("Duplicate Entry")
End If
Catch ex As Exception
MessageBox.Show($"Error Entering Attendance {ex.Message}")
End Try
End Sub
Be sure to check the datatypes of your parameters with the database.
You should create a function that queries your data table to return the row count where the Date, Subject, and StudentId match what you're wanting to insert. If the returned result is greater than 0 then display the message.
Here is a function that returns -1 if something went wrong or the row count:
Private Function GetRowCount(ByVal studentIdParameter As String, ByVal dateParameter As DateTime, ByVal subjectParameter As String) As Integer
'Declare the object to return
Dim count As Integer = -1
'Declare the connection object
Dim con As OleDbConnection
'Wrap code in Try/Catch
Try
'Set the connection object to a new instance
'TODO: Change "My Connection String Here" with a valid connection string
con = New OleDbConnection("My Connection String Here")
'Create a new instance of the command object
'TODO: Change [ID] to a valid column
Using cmd As OleDbCommand = New OleDbCommand("SELECT Count([ID]) FROM [MyTable] WHERE studentID=#studentId, AND date=#date AND subject=#subject", con)
'Parameterize the query
cmd.Parameters.Add("#studentId", OleDbType.NVarChar).Value = studentIdParameter
cmd.Parameters.Add("#date", OleDbType.DBDate).Value = dateParameter.Date
cmd.Parameters.Add("#subject", OleDbType.NVarChar).Value = subjectParameter
'Open the connection
con.Open()
'Use ExecuteScalar to return a single value
count = Convert.ToInt32(cmd.ExecuteScalar())
'Close the connection
con.Close()
End Using
Catch ex As Exception
'Display the error
Console.WriteLine(ex.Message)
Finally
'Check if the connection object was initialized
If con IsNot Nothing Then
If con.State = ConnectionState.Open Then
'Close the connection if it was left open(exception thrown)
con.Close()
End If
'Dispose of the connection object
con.Dispose()
End If
End Try
'Return the row count
Return count
End Function
To implement it, you'd do something like:
Dim rowCount = GetRowCount(TextBox1.Text, DateTimePicker1.Value, ComboBox1.Text)
If (rowCount = -1) Then
MessageBox.Show("Something went wrong checking for duplicates")
ElseIf (rowCount = 0) Then
' Insert record
Else
MessageBox.Show("A record already exists with this studentId, date, and subject.")
End If

Query Timeout Expired when updating SQL Server from VB,NET

I'm updating SQL Server from VB.NET and keep getting the 'Query Timeout Error', I have lot's of sub routines that I run in sequence that look like the following:
Public Shared Sub Update_DailyRatings()
Dim stallStats As String = ""
Dim win As Integer = 0
Dim mSplit As Array
Dim cn As OleDbConnection = New OleDbConnection(MainForm.connectStringPublic)
cn.Open()
Dim selectString As String = "Select * FROM DailyRatings"
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim reader As OleDbDataReader = cmd.ExecuteReader()
While (reader.Read())
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
End While
reader.Close()
cn.Close()
End Sub
The NonQuery sub looks like this:
Public Sub NonQuery(ByVal SQL As String)
Dim query As String = SQL
Try
Dim cn3 As OleDbConnection = New OleDbConnection(connectStringPublic)
cn3.Open()
Dim cmd As OleDbCommand = New OleDbCommand(query, cn3)
cmd.CommandTimeout = 90
cmd.ExecuteNonQuery()
cn3.Close()
cn3.Dispose()
cmd.Dispose()
OleDbConnection.ReleaseObjectPool()
Catch e As System.Exception
Clipboard.SetText(query)
MsgBox(e.Message)
Finally
End Try
End Sub
As you can see I've been trying ideas to fix this that I found in other threads such as extending the timeout and using the Dispose() and ReleaseObjectPool() methods but it hasn't worked, I still get query timeout error at least once when running all my subs in sequence, it's not always the same sub either.
I recently migrated from Access, this never used to happen with Access.
If you are dealing with Sql Server why are you using OleDb? I guessed that is was really access.
While your DataReader is open, your connection remains open. With the amount of processing you have going on, it is no wonder that your connection is timing out.
To begin, connections and several other database objects need to be not only closed but disposed. They may contain unmanaged resources which are released in the .Dispose method. If you are using an object that exposes a .Dispose method use Using...End Using blocks. This will take care of this problem even if there is an error.
Actually you have 2 distinct operations going on. First you are retrieving DailyRatings and then you are updating DailyRatings base on the data retrieved. So we fill a Datatable with the first chunk of data and pass it off to the second operation. Our first connection is closed and disposed.
In operation 2 we create our connection and command objects just as before except now our command has parameters. The pattern of the command is identical for every .Execute, only the values of the parameters change. This pattern allows the database, at least in Sql Sever, to cache a plan for the query and improve performance.
Public Shared Function GetDailyRatings() As DataTable
Dim dt As New DataTable
Using cn As New OleDbConnection(MainForm.connectStringPublic),
cmd As New OleDbCommand("Select * FROM DailyRatings", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
Public Sub UpdateDailyRatings()
Dim dt = GetDailyRatings()
Using cn As New OleDbConnection(connectStringPublic),
cmd As New OleDbCommand("UPDATE DailyRatings SET StallWin = #Stall WHERE Horse = #Horse")
cmd.Parameters.Add("#Stall", OleDbType.Integer)
cmd.Parameters.Add("#Horse", OleDbType.VarChar)
cn.Open()
For Each row As DataRow In dt.Rows
cmd.Parameters("#Horse").Value = row("Horse").ToString
Dim stallStats As String = Get_Stall_Stats(row("Track").ToString, CInt(row("Stall")), CDbl(row("Distance")))
If stallStats = "" Then
cmd.Parameters("#Stall").Value = 999
Else
cmd.Parameters("#Stall").Value = CInt(stallStats.Split(","c)(0))
End If
cmd.ExecuteNonQuery()
Next
End Using
End Sub
Private Function GetStallStats(Track As String, Stall As Integer, Distance As Double) As String
Dim s As String
'Your code here
Return s
End Function
Note: OleDb does not pay attention to parameters names. It is the order that they appear in the query statement must match the order that they are added to the Parameters collection.
It's possible that OleDbDataReader is locking your table or connection as it get the data with busy connection. You can store the data in a DataTable by using OleDbDataAdapter and loop through it to run your updates. Below is the snippet how your code would look like:
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim dt As New DataTable()
adapter.Fill(dt)
For Each reader As DataRow In dt.Rows
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
Next
cn.Close()

Running SQL Server query in VB.NET

How would I create a query that returns a column in the database? The SQL select statement is easy. I'm having issues with the VB side.
SELECT UserNO
FROM UserTable
WHERE UserID = user;
I need to then get that UserNO and pass it to another T-SQL stored procedure. How would I go about running a SELECT query and getting the results back in Visual Basic?
Attached is some of my code. The code below adds the user to the DB however the UserNo (INTEGER) is automatically generated by SQL Server as the INSERT statement is run in the insert stored procedure, so I need to pull the UserNO after the user is created.
Public conwd As SqlConnection = New SqlConnection("Server=*****;Database=*****;User Id=****;Password=****")
Public conwp As SqlConnection = New SqlConnection("Server=*****;Database=*****;User Id=****;Password=****")
Dim cmdP As SqlCommand = New SqlCommand("EXECUTE [dbo].[AddNewUserWestonTemp] '" + user + "'", conwp)
Dim cmdD As SqlCommand = New SqlCommand("EXECUTE [dbo].[AddNewUserWestonTemp] '" + user + "'", conwd)
conmp.Open()
conmd.Open()
cmdP.ExecuteNonQuery()
cmdD.ExecuteNonQuery()
The Using..End Using blocks close and dispose of your data objects that might contain unmanaged code. The Parameters help prevent SQL injection.
Private Sub OPCode2()
Dim newID As Integer
Dim sql = "Insert Into UserTable (User) Values (#User); Select SCOPE_IDENTITY();"
Using cn As New SqlConnection("Your Connection String")
Using cmd As New SqlCommand(sql, cn)
cmd.Parameters.Add("#User", SqlDbType.NVarChar).Value = "Brian Weaver"
cn.Open()
newID = CInt(cmd.ExecuteScalar)
End Using
End Using
'Use the newID in your next command
UserStoredProcedure(newID)
End Sub
Private Sub UseStoredProcedure(id As Integer)
Dim sql = "InsertSomeUserInfo" 'the name of your stored procedure
Using cn As New SqlConnection("Your Connection String")
Using cmd As New SqlCommand(sql, cn)
cmd.CommandType = CommandType.StoredProcedure
'whatever parameter names and types your stored procedure uses
cmd.Parameters.Add("#UserID", SqlDbType.Int).Value = id
cmd.Parameters.Add("#Salary", SqlDbType.Decimal).Value = 50000
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Using
End Sub
Dim cmdP As SqlCommand = New SqlCommand("EXECUTE [dbo].[AddNewUserWestonTemp] '" + user + "'" + "; SELECT CAST(scope_identity() AS int", conwp)
Dim userNo as Integer = cmdP.ExecuteScalar()
ExecuteScalar retrieves the first line of the result

Weird array issue.....need advice on this

I am reading data from an sql database and the connection happens but the following error occurs:
{"Index was outside the bounds of the array."}
On this line:
TextBox2.Text = TextBox2.Text & sqRdr.GetValue(22) & vbCrLf
Please help me with this as I have counted all the columns in my table and they have turned out to be exactly (22).
The column ordinal for the datareader is 0 based, so the 22nd column would be sqRdr.GetValue(21)
Your Datareader should be like below to avoid this issue in a case when you have increased/decreased the number of column in future....
DR["ColumnName"]
SqlDataReader's this[string name] looks like:
Public Overrides Default ReadOnly Property Item(name As String) As Object
Get
Return Me.GetValue(Me.GetOrdinal(name))
End Get
End Property
Below is the sample code...
Using con As System.Data.SqlClient.SqlConnection = New SqlConnection("YourConnection string")
con.Open()
Dim cmd As New SqlCommand()
Dim expression As String = "Parameter value"
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "Your Stored Procedure"
cmd.Parameters.Add("Your Parameter Name", SqlDbType.VarChar).Value = expression
cmd.Connection = con
Using dr As IDataReader = cmd.ExecuteReader()
'You code like ....dr["YourColumnName"]
If dr.Read() Then
End If
End Using
End Using

Inserting data into SQL Server database using VB.Net

I am currently using HDI Membership provider and the design looks as shown below:
Now I am trying to create a new user and insert those values into the database as shown below:
Try
Dim connectionString As String = "Data Source=.\sqlexpress;Initial Catalog=HDIMembershipProvider;Integrated Security=True"
Using cn As New SqlConnection(connectionString)
cn.Open()
Dim cmd As New SqlCommand()
cmd.CommandText = "INSERT INTO Users VALUES(#Username,#Password,#Email,#PasswordQuestion,#PasswordAnswer)"
Dim param1 As New SqlParameter()
param1.ParameterName = "#Username"
param1.Value = txtUsername.Text.Trim()
cmd.Parameters.Add(param1)
Dim param2 As New SqlParameter()
param2.ParameterName = "#Password"
param2.Value = txtPassword.Text.Trim()
cmd.Parameters.Add(param2)
Dim param3 As New SqlParameter()
param3.ParameterName = "#Email"
param3.Value = txtEmail.Text.Trim()
cmd.Parameters.Add(param3)
Dim param4 As New SqlParameter()
param4.ParameterName = "#PasswordQuestion"
param4.Value = txtSecurityQuestion.Text.Trim()
cmd.Parameters.Add(param4)
Dim param5 As New SqlParameter()
param5.ParameterName = "#PasswordAnswer"
param5.Value = txtSecurityAnswer.Text.Trim()
cmd.Parameters.Add(param5)
cmd.Connection = cn
cmd.ExecuteNonQuery()
cn.Close()
End Using
Successlbl.show
Successlbl.show.Text = "Regisration Success."
Catch
Errolbl.Show()
Errolbl.Text = "Your account was not created.Please try again."
End Try
Now the problem is the data is not inserting to the database. I would like to know If anyone can point me where I'm going wrong?
Your insert statement is incorrect - since you are not specifying any field names you should be supplying values for all columns.
The fix is to supply the names of the columns you are insert into.
The screenshot also shows that there is a required ApplicationName column, so unless it has a DEFAULT defined, you will need to supply that as well.
Assuming you have a DEFAULT defined on ApplicationName:
cmd.CommandText = "INSERT INTO Users ( Username, Password, Email, PasswordQuestion, PasswordAnswer) VALUES(#Username,#Password,#Email,#PasswordQuestion,#PasswordAnswer)"

Resources