sql query returning whole column of data - vb.net

I have a VB.net program that parses a set of variables into a sub that I use to communicate with an Access Database.
The query string reads:
SELECT ID FROM cbRooms WHERE #condition
#condition is given a value with the code:
.Parameters.AddWithValue("#condition", dbCondition)
Where dbCondition is, in this case:
H_Block=True
When I run this query within Access directly, I get the expected data back, which is a set of numbers, that have a ticked box in Access
However, when I run that same code in Visual Studio, it just returns the whole column, regardless of whether the tick box is true or not
It's been a while since i've written something in VB, so i'm quite rusty, and therefore the problem is likely a simple oversight on my part
Here is the code for the sql access function:
Public Function sqlSelect(ByVal dbCol As String, ByVal dbTable As String, ByVal dbCondition As String)
'Creating the sqlCmd string to sent as an SQL request to the Database
Dim sqlCmd As String
'Creating a new connection to the database
Dim conn As New OleDb.OleDbConnection
'Setting the value for the sqlCmd string, with several "#" parameters
sqlCmd = "SELECT " & dbCol & " FROM " & [dbTable] & " WHERE #condition;"
'Running the Connect Sub routine to interact with the database
Connect(conn)
Using conn
Using dbEvent As New OleDb.OleDbCommand
With dbEvent
'sets the connection used by the current instance of OleDB usng the conn string
.Connection = conn
'Sets how the .CommandType is interpreted
.CommandType = CommandType.Text
'Sets the sqlCmd string that will be sent
.CommandText = sqlCmd
'Setting the "#" parameters in the sqlCmd string using values parsed into the sub routine
.Parameters.AddWithValue("#condition", dbCondition)
End With
Try
'Opening connection to the database
conn.Open()
'Creating the _sqlRead Reader used to read the data coming from the database
Dim sqlReader As OleDb.OleDbDataReader
sqlReader = dbEvent.ExecuteReader()
'Creates a list that will store the values returned to the Search.vb class
Dim returnVals As New List(Of Integer)
Do While sqlReader.Read = True
returnVals.Add(sqlReader(dbCol))
Loop
Return returnVals
Catch ex As Exception
'Opens a message box showing the current error
MessageBox.Show(ex.Message.ToString(), "Error Message")
End Try
End Using
End Using
End Function
And Here is the code for the combo box that calls the AccessSQL.vb:
Public Sub cbBlock_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbBlock.SelectedIndexChanged
Select Case cbBlock.Text
Case "H Block"
cbRoom.Items.Clear()
Dim listOfRoomsInBlock As New List(Of Integer)
listOfRoomsInBlock = sqlSelect("ID", "cbRooms", "H_Block=True")
For x = 0 To listOfRoomsInBlock.Count - 1
cbRoom.Items.Add(listOfRoomsInBlock(x))
Next
Case "Tech Block"
cbRoom.Items.Clear()
Dim listOfRoomsInBlock As New List(Of Integer)
listOfRoomsInBlock = sqlSelect("ID", "cbRooms", "T_Block=True")
For x = 0 To listOfRoomsInBlock.Count - 1
cbRoom.Items.Add(listOfRoomsInBlock(x))
Next
End Select
End Sub
Thanks for any help!
EDIT:
The SQL, when run in VB returns This into the combo box I'm trying to populate
The 1 - 13 values at the bottom is what I'm expecting, whereas the 14 - 30 is not. My thinking is that my parameterisation is never actually using the condition I set above.
However, when I run that same, expected query, inside Access, I get the right values returned

Related

How to read date and time from database and compare it to the system date and time

I am working on a project using vb and one of my forms has to display the current active reminders (reminders that haven't hit their deadline yet) into a datagridview and I have another datagridview for reminders that are past the deadline. The date and time along with reminder information is saved into my access database and I want to read the date and time from database and compare it to the system date and time and then display the reminder information.
This is how my form looks like; the top datagridview is for current reminders and the bottom one is for past/out of date reminders:
This the code for my form and what I’ve tried:
Imports System.Data.OleDb
Public Class frmReminderInfo
Private Sub frmReminderInfo_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DisplayCurrentReminders()
End Sub
Private Sub DisplayCurrentReminders()
Dim ReminderDateTime As Date
Dim CurrentDateTime As Date
CurrentDateTime = Date.Now
CurrentDateTime = FormatDateTime(Date.Now, DateFormat.GeneralDate)
ReminderDateTime = FormatDateTime(ReminderDateTime, DateFormat.GeneralDate)
If DbConnect() Then
DgvCurrentReminders.Rows.Clear()
Dim SQLCmd As New OleDbCommand
With SQLCmd
.Connection = cn
.CommandText = "SELECT ReminderDate FROM TblReminder "
Dim rs As OleDbDataReader = .ExecuteReader()
While rs.Read
ReminderDateTime = (rs(0).ToString)
End While
End With
End If
cn.Close()
If CurrentDateTime = ReminderDateTime Then
Dim SQLCmd As New OleDbCommand
With SQLCmd
Dim rs As OleDbDataReader = .ExecuteReader()
While rs.Read
Dim NewStockRow As New DataGridViewRow()
NewStockRow.CreateCells(DgvCurrentReminders)
NewStockRow.SetValues({rs("ReminderID"), rs("CustomerName"), rs("DeviceInfo"), rs("RepairPrice"), rs("ReminderDate")})
NewStockRow.Tag = rs("ReminderID")
DgvCurrentReminders.Rows.Add(NewStockRow)
End While
rs.Close()
End With
End If
cn.Close()
End Sub
End Class
Disposable database objects like Connection should be declared locally in the method where they are used in a Using block.
You don't seem to have any idea of what the parts of an Sql string mean. The Select portion list the fields you want to retrieve. A star (*) in this clause means select all the fields. Here we are using a Where clause to filter the records. Only records where the field ReminderDate is greater than or equal to a parameter will be returned. This will only work if the data has been inserted properly as a DateTime.
The While loop keeps overwriting the value of ReminderDateTime on each iteration so only the last value returned by the reader will remain. Also, you are trying to force a String into a variable declared as a Date. Won't work.
Assuming the code could get beyond If CurrentDateTime = ReminderDateTime Then you would be working with a closed connection. Commands can't execute on a closed connection.
You also don't seem to have an idea how class objects work. Dim SQLCmd As New OleDbCommand Here you declare a new instance of the Command. You have no connection and no CommandText so it can't possibly be executed.
Take a look at the following code until it starts to make sense. Look up what Using blocks do. Look up the Load method of a DataTable to see what it does. Check out what a DataSource property has to offer.
Private Sub frmReminderInfo_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt = GetCurrentReminders()
DgvCurrentReminders.DataSource = dt
End Sub
Private Function GetCurrentReminders() As DataTable
Dim dt As New DataTable
Using cn As New OleDbConnection("Your connection string"),
SQLCmd As New OleDbCommand("SELECT * FROM TblReminder Where ReminderDate >= #Date", cn)
SQLCmd.Parameters.Add("#Date", OleDbType.Date).Value = Now
cn.Open()
Using reader = SQLCmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function

How can I add the value of a single Cell from Access database to a string variable

As the title says I have a MS Access database from where I need to find a specific dataset determined by a String Value. the reason for having to do this is so I can find the value of a single cell in this datase which has to be used as a path to find a certain file. my approach so far is the following:
Dim conn As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ExaptLokal.accdb")
Dim cmd As New OleDb.OleDbCommand
Dim dt As New DataTable
Dim da As New OleDb.OleDbDataAdapter
Dim sql As String
sql = "SELECT NC_KEY FROM EXAPT_NC_KOPF_DATEN WHERE NC_PROGRAMM_NAME =" & ProgrammNr.Text.ToString
MsgBox(sql)
conn.Open()
cmd.Connection = conn
cmd.CommandText = sql
da.SelectCommand = cmd
da.Fill(dt)
fullpath = dt.ToString
at the end I would like to have the result from my SQL Query as the value of my "fullpath" variable but so far the da.Fill(dt) row is giving me a hard time saying there is a conflict with the datatype.
Is the datatable even needed in this case or might I be able to skip that step and get the result of the query directly in the fullpath variable?
Thanks to everyone in advance
Edit: Thanks for the help (though not the friendliest but who am I to judge) I finally got it to work with the Execute Scalar method. I would just wish newbies to this website would be greeted a little better lol
have a great day
I moved the connection string to a class level variable so you can use it in other methods.
I separated your data access code from your user interface code passing the value from the text box to a function that returns the path.
I changed your select statement to use parameters. Always use parameters to avoid sql injection and avoid errors.
Use Using...End Using blocks to ensure that your database objects are closed and disposed. This Using block covers both the command and the connection.
You can pass the .CommandText and the .Connection directly to the constructor of the command.
When adding parameters to the parameters collection you provide the parameter name, the datatype form the database, and the size of the field. I had to guess at the type and size so, check your database for the actual values.
Since you are expecting a single value you can use .ExecuteScalar.
Private ConStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ExaptLokal.accdb"
Private Function GetPath(ProgrammNr As String) As String
Dim fullpath As String
Dim sql = "SELECT NC_KEY FROM EXAPT_NC_KOPF_DATEN WHERE NC_PROGRAMM_NAME = #ProgrmmNr"
Using conn As New OleDb.OleDbConnection(ConStr),
cmd As New OleDb.OleDbCommand(sql, conn)
cmd.Parameters.Add("#ProgrmmmNr", OleDbType.VarChar, 100).Value = ProgrammNr
conn.Open()
fullpath = cmd.ExecuteScalar.ToString
End Using
Return fullpath
End Function
Usage...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fullpath = GetPath(ProgrammNr.Text)
End Sub

Must declare the scalar variable "#SSTGroupID"

Private Function GetSvcType(ByVal oCommand As OleDbCommand, ByVal SSTGroupID As Integer) As DataTable
Dim sSQL As New StringBuilder
sSQL.AppendLine(" Select SSTServiceTypeID AS ID, SSTServiceTypeName AS Name ")
sSQL.AppendLine(" from fgen_SSTServiceType (nolock) ")
sSQL.AppendLine(" Where 1=1 AND Disabled = 0 ")
sSQL.AppendLine(" AND fgen_SSTServiceType.SSTGroupID = #SSTGroupID ")
oCommand.Parameters.AddWithValue("#SSTGroupID", SSTGroupID)
Return GetDataTable(sSQL.ToString)
End Function
Private Function GetDataTable(ByVal SQL As String) As DataTable
Dim oConn As OleDbConnection = New OleDbConnection(_strConnection)
Dim oCommand As New OleDbCommand("", oConn)
oCommand.Connection.Open()
oCommand.CommandText = SQL
oCommand.Parameters.Clear()
Dim oDataTable As New DataTable
Dim oDataAdapter As New OleDbDataAdapter(oCommand)
oDataAdapter.Fill(oDataTable)
If oDataTable.Rows.Count > 0 Then
GetDataTable = oDataTable
Else
GetDataTable = Nothing
End If
oCommand.Connection.Close()
oCommand.Dispose()
End Function
I've been searching for hours on end and can't seem to find a solution. I need your help please thanks
I've updated my question include the GetDataTable function. Please take a look thanks.
Your command never gets the text from the StringBuilder. So I think the missing link is that you should assign the string you've built to the command text
oCommand.CommandText = sSQL.ToString()
then add the parameter after that
Private Function GetSvcType(ByVal oCommand As OleDbCommand, ByVal SSTGroupID As Integer) As DataTable
Dim sSQL As New StringBuilder()
sSQL.AppendLine(" Select SSTServiceTypeID AS ID, SSTServiceTypeName AS Name ")
sSQL.AppendLine(" from fgen_SSTServiceType (nolock) ")
sSQL.AppendLine(" Where 1=1 AND Disabled = 0 ")
sSQL.AppendLine(" AND fgen_SSTServiceType.SSTGroupID = #SSTGroupID ")
oCommand.CommandText = sSQL.ToString()
oCommand.Parameters.AddWithValue("#SSTGroupID", SSTGroupID)
Return GetDataTable(oCommand.CommandText)
End Function
Alternatively, you may want to use a Using to create a command and dispose it. I'd write it but I don't see your connection so you should look into this answer for an example.
I wasn't sure what the Where 1 = 1 and the (no lock) were doing so I removed them.
The function FillDataTable contains all your database access code which keeps it separate from the User Interface code. Your database objects should be locale so you can control that they are closed and disposed. The Using...End Using block takes care of this even if there is an error. Get rid of any class level variables for commands and connections. Both the command and connection are included; note the comma at the end if the first line of the Using.
You can pass your connection string directly to the constructor of the connection and pass the command text and connection directly to the constructor of the command. Saves having to set these properties individually.
OleDb pays no attention to the name of the parameter, so, the order that the parameter is added to the Parameters collection must match the order that the parameter appears in the command text. In this case, you have only one but just for future reference. It is better to use the Parameters.Add() which includes the database datatype. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
Note: I had to guess at the datatype of your parameter. Check your database for the actual type.
Always open your connection at the last possible moment (the line before the .Execute...) and close it as soon as possible (the End Using)
Private Function FillDataTable(GroupID As Long) As DataTable
Dim strSQL = "Select SSTServiceTypeID AS ID, SSTServiceTypeName As Name
From fgen_SSTServiceType
Where Disabled = 0
And SSTGroupID = #SSTGroupID "
Dim dt As New DataTable
Using cn As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand(strSQL, cn)
cmd.Parameters.Add("#SSTGroupID", OleDbType.BigInt).Value = GroupID
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dt = FillDataTable(7L) 'the L indicates that this is a long,pass the GroupID to the function
DataGridView1.DataSource = dt
End Sub
EDIT
Dim dt = FillDataTable(7L) 'In the button code
And in the Data Access code change Oledb to Sql
Imports System.Data.SqlClient
Class DataAccess
Private Function FillDataTable(GroupID As Long) As DataTable
Dim strSQL = "Select SSTServiceTypeID AS ID, SSTServiceTypeName As Name
From fgen_SSTServiceType
Where Disabled = 0
And SSTGroupID = #SSTGroupID "
Dim dt As New DataTable
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand(strSQL, cn)
cmd.Parameters.Add("#SSTGroupID", SqlDbType.BigInt).Value = GroupID
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
End Class

Converting VBA function to VB.net to get sql data

I am trying to convert VBA code into VB.net and I have made it to a point but I can't convert resultset into vb.net. RS was 'dim as resultset' in VBA, thought i could just change it to dataset but am getting errors with the '.fields' and other options?
Function GetG(sDB As String, sServ As String, sJob As String) As String
'sDB = Database name, sServ = Server\Instance, path = job.path
Dim conString As String = ("driver={SQL Server};server = " &
TextBox1.Text & " ; uid = username;pwd=password:database = " &
TextBox2.Text)
Dim RS As DataSet
Dim conn As SqlConnection = New SqlConnection(conString)
Dim cmd As SqlCommand
conn.Open()
'This is where my problems are occuring
cmd = New SqlCommand("SELECT [ID],[Name] FROM dbo.PropertyTypes")
Do While Not RS.Tables(0).Rows.Count = 0
If RS.Fields(1).Value = sJob Then
GetG = RS.Fields(0).Value
GetG = Mid(GetG, 2, 36)
Exit Do
End If
DataSet.MoveNext
Loop
conn.Close
End Function
Based on my understanding and some guesswork, here is what I came up with for what I think you're wanting.
As I stated in my comment above, it appears you can just use a WHERE clause to get the exact record you want (assuming a single instance of sJob appears in the name column).
Build the connectionstring off the input arguments, not controls on your form. That is after all why you allow for arguments to be passed along. Also note that there is a SqlCommandBuilder object that may be of interest. But for now
Function GetG(sDB As String, sServ As String, sJob As String) As String
'we'll pretend your connectionstring is correct based off of the sDB and sServ arguments
Dim conStr As String = ("driver={SQL Server};server = " & sServ & " ; uid = username;pwd=password:database = " & sDB)
'Create a connection and pass it your conStr
Using con As New SqlConnection(conStr)
con.Open() 'open the connection
'create your sql statement and add the WHERE clause with a parameter for the input argument 'sJob'
Dim sql As String = "SELECT [ID], [Name] FROM dbo.PropertyTypes WHERE [Name] = #job"
'create the sqlCommand (cmd) and pass it your sql statement and connection
Using cmd As New SqlCommand(sql, con)
'add a parameter so the command knows what #job holds
cmd.Parameters.Add(New SqlParameter("#job", SqlDbType.VarChar)).Value = sJob
'Now that have the command built, we can pass it to a reader object
Using rdr As SqlDataReader = cmd.ExecuteReader
rdr.Read()
'i admin i'm a little confused here on what you are
'trying to achieve so ID may not be what you are
'really wanting to get a substring of.
Return rdr("ID").ToString.Substring(2, 36)
End Using
End Using
End Using
End Function
An example to see if this is working could be to call a messagebox do display the result. For this example, I'm going to pretend that TextBox3 holds the sJob you're wanting. With that knowledge, you could simply do:
MessageBox.Show(GetG(TextBox2.Text, TextBox1.Text, TextBox3.Text))
This should then produce the result in a messagebox.
It seems that you're not filling your DataSet. So, when you try to loop through it, it's uninitialized or empty.
Check this answer to see an example: Get Dataset from DataBase

"No data exists for the row/column" when connecting to SQL database from VB.net

I'm trying to create a program which has a datagridview, when the user clicks on a cell in the view, it then looks in a SQL database, grabs information from other fields in the same record, and automatically fills corresponding text boxes (done by manipulating the name of the field) in the form.
For some reason however, I'm getting an error message saying:
"InvalidOperationException was unhandled"
"No Data exists for the row / column"
Here is the code relevant to this part of the program:
Private Sub DataGridView1_CellMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvResults.CellMouseClick
' Set values in the text boxes to both corresponding to the film.
Dim strFields() As String = {"ID", "fName", "fGenre", "fSynopsis", "fAgeRating", "fActorsActresses", "fWebRating", "fNoReservations", "fWeeksRunning"}
Dim Con = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=ApplicationData.accdb;Persist Security Info=False;")
Con.Open() 'Open the connection
Dim Cmd As New OleDbCommand(StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName"), Con) 'Create a string by calling the StringBuilderCommand to combine the parameters together with quotes.
Cmd.CommandType = CommandType.Text
Dim Rdr As OleDbDataReader = Cmd.ExecuteReader()
Dim intCount As Integer = 4 ' Create a loop variable.
Do While Rdr.Read() Or intCount < 6 ' While this statement is 'TRUE', e.g. there is a valid record.
strResult = "txt" & strFields(intCount).Replace("f", "") 'Remove any instances of 'f', e.g. the prefix of the string.
txtActorsActresses.Text = StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName")
Me.Controls(strResult).Text = Rdr.Item(strFields(intCount)) ' Suspect the error lies here.
'Set the text-box to the correct value from the database.
'This will allow me to go through several text boxes, and grab their corresponding values from the database.
intCount = intCount + 1
'Current error is because it cannot find any data beyond the first field taken.
'I have no idea why this is. But if I change the starting intCount value, it will successfully take a different value.
Loop
Rdr.Close() 'Cleaning up.
Cmd.Dispose()
Con.Close()
WebBrowser1.Navigate(dgvResults.CurrentCell.Value.Replace(" ", ".") & ".movie.poster.new.jpg.to") 'Grab the movie poster off the internet corresponding to the films name.
End Sub
Private Function StringBuilderCommand(Field, Table, CurrentCellValue, SearchParameter)
'Creates a suitable SQL string.
Dim MyStringBuilder As New StringBuilder("SELECT ")
MyStringBuilder.Append("*") ' Append the parameter 'Field'.
MyStringBuilder.Append(" FROM ") ' Append the SQL command 'FROM'.
MyStringBuilder.Append(Table) ' Append the parameter 'Table'.
MyStringBuilder.Append(" WHERE ") ' Append the SQL command 'WHERE'.
MyStringBuilder.Append(SearchParameter) ' Append the parameter 'SearchParameter'.
MyStringBuilder.Append("=""")
MyStringBuilder.Append(CurrentCellValue) ' Append the parameter 'CurrentCellValue', representing the cell selected.
MyStringBuilder.Append("""") 'Append a quotation mark.
Return MyStringBuilder.ToString() ' Return it to the main program.
End Function
Database table being connected to:
A view of the error as it looks in Visual Studio 2012 Express:
The value of 'dgvResults.CurrentCell.Value' is the name of a film taken from the database (e.g. "12 Years a Slave").
What am I doing wrong?
Thanks,
C.
The problem is caused by the value of strFields(intCount) you are passing to the reader. It is not a valid column index.
You probably want to loop on the fields before looping again on DataReader(), like:
Do While Rdr.Read()
For intCount as Integer = 4 to 6
strResult = "txt" & strFields(intCount).Replace("f", "")
txtActorsActresses.Text = StringBuilderCommand("*", "Films", dgvResults.CurrentCell.Value, "fName")
Me.Controls(strResult).Text = Rdr.Item(strFields(intCount))
Next
Loop
I removed the Dim intCount As Integer = 4 because it is no longer needed because of the for next loop.