unable to find specified column in result set index out of bounds index out of range exception - sql

i am having trouble putting a value in a textbox. Each time a ticket is sold i put the total price in a textbox, each time a ticket is sold for the same concert it increases by adding its self to the total price. It works at the first sale, but after that it breaks down. here is the code and thanks in advance.
Private Function DisplayMoneyTaken() As Integer
Dim totalMoney As Integer
'open the database connection
strSQL = "SELECT MAX(Total_Money) FROM Sales WHERE Concert_Id =" + Mid(cboVenue.Text, 1, 4)
conn.Open()
cmd.Connection = conn
cmd.CommandText = strSQL
cmd.CommandType = CommandType.Text
dr = cmd.ExecuteReader()
'read the record returned
dr.Read()
If IsDBNull(dr.Item(0)) Then
totalMoney = txtPrice.Text
Else
DisplayMoneyTaken = dr.Item("Total_Money") + Val(txtPrice.Text)
End If
'close the database
conn.Close()
Return totalMoney
End Function

It doesn't appear that your query has a column named "Total_Money". You didn't name the single column your query returns.

Michael,
When using an aggregate function, it is necessary to also assign an alias to that column.
For example
strSQL = "SELECT MAX(Total_Money) as CaChing FROM Sales WHERE Concert_Id =" + Mid(cboVenue.Text, 1, 4)
If you do not assign an alias, the server will sometimes assign one. But you have to know or guess what it will be. Its a lot better to just pick one that makes sense.
You can also index items with a number, this is what you did when you were checking for NULLs.
This same syntax could have been used when accessing the value.
DisplayMoneyTaken = dr.Item(0) + Val(txtPrice.Text)

Related

Select value and display in a combobox if the datetimepicker value lies between two extreme dates in a table in database

I am creating an attendance management system in vb.net and using MS access as my database. I created a leave form in which the student on leave has a roll number and his details along with the from date and to date field.
What I'm trying to do is to show all the roll numbers of students in the ComboBox on leave if the DateTimePicker value is in between the to date and from date, the command that I created as MS access query is selecting the from date and to date ie., extreme dates matching with the DateTimePicker value but not showing the values if date is between the to and from date.
this is the code and query:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
con.Open()
Dim cmd1 As New OleDbCommand("Select roll_no From leaves Where semester= " + ComboBox3.SelectedItem + " and (from_date<= #" & DateTimePicker1.Value.ToShortDateString & "# and to_date >= #" & DateTimePicker1.Value.ToShortDateString & "#)", con)
Dim da1 As New OleDbDataAdapter
da1.SelectCommand = cmd1
Dim dt1 As New DataTable
dt1.Clear()
da1.Fill(dt1)
ComboBox4.DataSource = dt1
ComboBox4.DisplayMember = "roll_no"
ComboBox4.ValueMember = "roll_no"
con.Close()
End Sub
Is there any modification in query through which I can get my desired results to get all the roll no if DateTimePicker value is between dates in database?
Well, you probably do want to wrap the code in a using block - that way if you have a error, it will STILL close the connection. Also, it means you don't have to close the connection - a using block ALWAYS will (so, this costs you ONLY one extra line of code, but it more robust - will not leave stray connections open.
Next up:
While everyone warns and suggests to try and avoid string concertation for reasons of SQL injection?
Actually, the BETTER case is that string concatenation is tricky, messy, and you have to remember to use quotes to surround strings, maybe "#" for dates, and for numbers no surrounding delimiters. And one tiny missing single quote or whatever, and your sql is now wrong. So, this is a BETTER case and reason to use parameters - to allow you to write code, and write code that is easer to fix, read, maintain, and even add more parameters to without complex string concatenations.
So, I would suggest this:
Using conn As New OleDbConnection(My.Settings.AccessDB)
Dim strSQL As String =
"Select roll_no From leaves Where from_date >= #cboDate and to_date <= #cboDate " &
"AND semester = #Semester"
Using cmd = New OleDbCommand(strSQL, conn)
cmd.Parameters.Add("#cboDate", OleDbType.DBDate).Value = DateTimePicker1.Value
cmd.Parameters.Add("#Semester", OleDbType.Integer).Value = ComboBox3.SelectedValue
conn.Open()
ComboBox4.DisplayMember = "roll_no"
ComboBox4.ValueMember = "roll_no"
ComboBox4.DataSource = cmd.ExecuteReader
End Using
End Using
And note how I dumped the need for a data adaptor - don't' need it.
And note how I dumped the need for a data table - don't' need it.
However, you do OH SO VERY often need a data table. So, since we humans do things over and over without thinking - memory muscle - then I would suggest that it is ok to create and fill a data table and shove that into the "on leave".
So since we "often" will need a data table, then for the sake of learning, then you could write it this way (so we now learn how to fill a data table).
Using conn As New OleDbConnection(My.Settings.AccessDB)
Dim strSQL As String =
"Select roll_no From leaves Where from_date >= #cboDate and to_date <= #cboDate " &
"AND semester = #Semester"
Using cmd = New OleDbCommand(strSQL, conn)
cmd.Parameters.Add("#cboDate", OleDbType.DBDate).Value = DateTimePicker1.Value
cmd.Parameters.Add("#Semester", OleDbType.Integer).Value = ComboBox3.SelectedValue
conn.Open()
Dim dt1 As New DataTable
dt1.Load(cmd.ExecuteReader)
ComboBox4.DisplayMember = "roll_no"
ComboBox4.ValueMember = "roll_no"
ComboBox4.DataSource = dt1
End Using
End Using
But, either way? Note how I did not have to remember, think about, worry about, and try to figure out the delimiters. Is that a " # " we need for dates, or is that a " ' " we need around the date?
Of course this code would be placed in the timepicker value changed event.

Naming Column Header Based On Results From Database

net and would to have the Header Text of columns in a datagridview be named after results from the database, e.g the query in my code returns four dates,30/08/2017,04/09/2017,21/09/2017 and 03/02/2018. My aim is to have the column headers in the data grid named after those dates. Your help will highly be appreciated.
sql = "SELECT COUNT (ServiceDate) As NoOfServiceDates FROM (SELECT DISTINCT ServiceDate FROM tblattendance)"
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
reader.Read()
ColumnNo = CInt(reader("NoOfServiceDates")).ToString
End Using
End Using
DataGridView1.ColumnCount = ColumnNo
For i = 0 To DataGridView1.Columns.Count - 1
sql = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
End While
End Using
End Using
Next
The current code re-runs the query each time through the column count loop, meaning it will set the column header for that column to all of the date values in sequence, so the last value in the query shows in the all the columns. You only need to run the query once:
Dim i As Integer = 0
sql = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using command As New OleDbCommand(sql, connection), _
reader As OleDbDatareader = command.ExecuteReader()
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
i+= 1
End While
End Using
Additionally, this still results in two separate trips to the database, where you go once to get the count and again to get the values. Not only is this very bad for performance, it leaves you open to a bug where another user changes your data from one query to the next.
There are several ways you can get this down to one trip to the database: loading the results into memory via a List or DataTable, changing the SQL to include the count and the values together, or adding a new column each time through the list. Here's an example using the last option:
DataGridView1.Columns.Clear()
Dim sql As String = "SELECT DISTINCT ServiceDate FROM tblattendance"
Using connection As New OleDbConnection("string here"), _
command As New OleDbCommand(sql, connection)
connection.Open()
Using reader As OleDbDataReader = command.ExecuteReader()
While reader.Read
Dim column As String = reader("ServiceDate").ToString()
DataGridView1.Columns.Add(column, column)
End While
End Using
End Using
Even better if you can use something like Sql Server's PIVOT keyword in combination with the DataGridView's AutoGenerateColumns feature for DataBinding, where you will write ONE SQL statement that has both column info and data, and simply bind the result set to the grid.
The For Next is incorrect. You execute your command for every column, when you only need to execute it once. The last result from the DataReader will be the header for every column as currently written.
You should iterate through your DataReader and increment the cursor variable there:
Dim i As Integer = 0
Using command = New OleDbCommand(sql, connection)
Using reader = command.ExecuteReader
While reader.Read
DataGridView1.Columns(i).HeaderText = reader("ServiceDate").ToString
i += 1
End While
End Using
End Using

VB.Net and Access - Getting Totaliser Value

I'm trying to databind a textbox to a totaliser value in an access database. I currently update the database via OleDbCommand and then edit any existing entries via databinding on the form.
I have everything working fine, but I want a textbox to show the totaliser (sum) of a particular column in the datbase. Access shows this totaliser underneath the column if the database is opened.
Is there a method to bind this value to the textbox?
Thanks
Well, however you are accessing the database, you need to make a call to get the SUM of your desired table.
If we are talking about SQL, it would look something like:
conn = New OleDbConnection(Get_Constring)
conn.Open()
cmd.Connection = conn
cmd.CommandType = CommandType.Text
sSQL = " SELECT SUM(total) AS Total From YourTable"
cmd.CommandText = sSQL
OleDbDataReader dr = cmd.ExecuteReader()
If dr.Read() Then
set total = Convert.ToInt32(dr["Total"])
End If
You could load this into a DataTable/DataSet or use the DataReader and assign the result (the sum) to a textbox, like:
TextBox1.Text = total
If you are using Linq it could look like (this is just an example):
Dim yourObject = From cust In db.Customers
Group By cust.City
Into Average(cust.Orders.Count)
Order By Average
DataGridView1.DataSource = yourObject

Can't generate a StockID above 10

This is the code that tries to grab the largest StockID from the database (Access database) , but my problem is that it generates StockID's up to "S10", after this it simply doesn't increment any further. This is the subroutine that generates the StockID:
Sub generate_Stock_ID()
Dim Stock_start As String = "S"
Dim Stock_Gen As String = "SELECT MAX(StockID) FROM tblStock WHERE StockID LIKE '" & Stock_start & "%%%' "
Dim da As OleDbDataAdapter = New OleDbDataAdapter(Stock_Gen, conn)
Dim ds As DataSet = New DataSet
da.Fill(ds, "StockID")
Dim dt As DataTable = ds.Tables("StockID")
Dim count As Integer = ds.Tables("StockID").Rows.Count
If ds.Tables("StockID").rows.count = 0 Then
StockID = "S1"
Else
StockID = ds.Tables("StockID").Rows(0).Item(0)
StockID = StockID.Substring(1, (StockID.Length - 1))
StockID = Stock_start & (StockID + 1)
End If
End Sub
Screenshot of my database
Note* there are multiple ID's for various other subroutines which all share the same incrementation issue, so if i fix this i fix the other ones too. So at the moment i think my problem lies in the syntax of my SQL statement, but im open to suggestions.
Thanks!
Don't treat an Integer as String. Otherwese MAX or ORDER BY will use lexicographical instead of numerical order which means that S11 is "lower" than S2.
So you should make this column an int-column and prepend S only where you display it. Then MAX(StockID) returns an Integer, you just have to cast it and add 1:
Using conn As New OleDbConnection("Connection-String")
Using cmd As New OleDbCommand(Stock_Gen, conn)
conn.Open()
Dim stockIDObj As Object = cmd.ExecuteScalar()
If stockIDObj IsNot Nothing Then
Dim maxStockId As Int32 = DirectCast(stockIDObj, Int32)
maxStockId += 1
' ...... '
End If
End Using
End Using
You should also change OPTION STRICT to ON. Then this would never compile since the same variable cannot be used for an Object, String and Integer which is very good since it prevents errors.
If you want to keep it as string you have to cast the substring always in the database which is less readable and less efficient. I also don't know how to do it in access.
If you want to change the type of column in an already populated table you should first add a new column with a similar name which is of type int. If all have S at the beginning you could first remove that, then you can update the new column with the casted int value. Finally you can delete the old column and rename the new to the old.
The root of this issue that StockID is a STRING and 'S1'>'S10' so for all StockId > 10 you get max = 'S1'.
As a fast fix try to change MAX(StockID) to:
SELECT 'S'+CAST(MAX(CAST(SUBSTRING(StockID,2,100) as int)) as varchar(100))
For ACCESS DB try to use:
SELECT "S" & cstr(MAX(CINT(MID(StockID,2,100))))

how to get data to textbox from the database

I have a form with one combo box and text box, and an SQL database
named balance with two columns; one as customername and the other as obbalance.
I had bound all of the customer name to the combo box, now what I have to do is,
when a user selects a customer name from the combo box, the text box should show the obbalance of the selected customername; here, the customer name will not be repeated - only one name per customer.
What can I do? Please help me.
Dim conectionstring As String
conectionstring = "Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\SHOPPROJECT\SHOPPROJECT\shop.mdf;Integrated Security=True;User Instance=True"
Dim ST As String = ComboBox1.SelectedText
Dim sqlcon As New SqlConnection(conectionstring)
Dim sqlcmd As New SqlCommand("SELECT OBBALANCE FROM BALANCE WHERE CUSTOMERNAME = " & " '" & ST & "'" & "", sqlcon)
MessageBox.Show(TextBox1.Text)
Dim result As Object
Try
sqlcon.Open()
' Dim sdr As SqlDataReader = sqlcmd.ExecuteReader()
result = sqlcmd.ExecuteScalar()
If result IsNot Nothing Then
TextBox1.Text = result.ToString()
MessageBox.Show(TextBox1.Text)
End If
Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Sub
I've tried this, but I can't see the value in the text box, and obbalance is a floating-point value from the SQL database.
If you're updating a text box, is this a single result (scalar value)? If so, the first thing I'd do is use ExecuteScalar not ExecuteReader. Then, use debug mode with breakpoints to get a better idea of what is actually happening. It may simply be that you're not getting any results.
Note: I'm assuming the bad coding practice (in-line sql statement, hard-coded connection string, etc.) are for clarity. If they're not, fix them.
Make the follwing changes:
Dim sqlcmd As New SqlCommand("SELECT OBBALANCE FROM BALANCE WHERE CUSTOMERNAME = '" & ST & "'", sqlcon)
TextBox1.Text = sdr.GetString(yourColumnIndex)
ComboBox1.SelectedText returns the highlighted (selected) text on the ComboBoxControl. That will be empty if you haven't use your mouse to select a portion of its text or hold the shift while you are pressing the direction keys on your keyboard. And that's probably why your query returns ZERO RECORDS.
Use the following instead:
Dim ST As String = ComboBox1.SelectedItem.Text
Set a breakpoint and ensure you are getting the value for OBBALANCE (see if you are getting any rows period might be good). Also, make sure you can only get one row, as you are iterating forward, even when you only need one value.
Better yet, consider ExecuteScalar, which only returns a single value. While you are at it, parameterize the SQL query so you don't get SQL injected.
UPDATE: Just change it here:
sdr = sqlcmd.ExecuteReader()
to something like
Dim s as String = sqlcmd.ExecuteScalar()
Then use s as your textbox value. You may have to ToString() the value or otherwise cast as string, as I believe the ExecuteScalar() returns an object.