Loop through action - sql

I am new to visual basic, however I need to loop through rows in a data table and use the values to in a test script, the script is as follows -
Public Function TestMain(ByVal args() As Object) As Object
StartApp(URL)
' HTML Browser '
Browser_HtmlBrowser(Document_HomePage(),DEFAULT_FLAGS).Maximize()
Button_AddNewProfilesubmit().Click()
'here is where the rows would be read and the loop would start'
Text_Ctl00MainContentProfileNa().Click(AtPoint(6, 13))
Browser_HtmlBrowser(Document_Http1921685526UserCon(), DEFAULT_FLAGS).InputChars("dataBase_Row_Value")
Table_HtmlTable_1().Click(AtCell( _
AtRow(AtIndex(0)), _
AtColumn(AtIndex(1))))
'here is where the loop would end after all rows had been read'
Return Nothing
End Function
I have an idea to achieve this, first doing a database connection, then create the loop -
Dim pName As String
Dim datas As DataSet
Dim datar As DataRow
Dim oledat As SqlDataAdapter
oledat = New SqlDataAdapter("SELECT COLUMN FROM DATABASE",ConnectionString)
oledat.Fill(datas)
For Each datar In datas.Tables(0).Rows
pName = datar.Item("PROFILENAME")
Text_Ctl00MainContentProfileNa().Click(AtPoint(6, 13))
Browser_HtmlBrowser(Document_Http1921685526UserCon(), DEFAULT_FLAGS).InputChars(pName)
Table_HtmlTable_1().Click(AtCell( _
AtRow(AtIndex(0)), _
AtColumn(AtIndex(1))))
Next
However this is breaking, even though there are no errors in Visual Studio, there is only the warning that datas is used before it is assigned the values. Where am I going wrong?

I believe you must initialize a new dataset before working with it. Example:
Dim ds As DataSet = New DataSet()
Dim connection As OleDb.OleDbConnection
Dim command As OleDb.OleDbCommand
Dim adapter As New OleDb.OleDbDataAdapter
Dim connString As String = "my Connection string stuff;"
connection = New OleDb.OleDbConnection(connString)
Try
'open the connection
If connection.State = ConnectionState.Open Then
Else
connection.Open()
End If
'fill each data table
command = New OleDb.OleDbCommand(selectOne, connection)
adapter.SelectCommand = command
adapter.Fill(ds, "someTableName")
Catch ex As OleDb.OleDbException
'error, do something
Finally
'close everything down
adapter.Dispose()
If (Not command Is Nothing) Then
command.Dispose()
End If
connection.Close()
End Try
This example uses OLEDB but should be comparable to what you are doing. Once you fill it, you should be able to iterate over the tables. But, first, check to make sure you have a dataset created first:
If (ds IsNot Nothing) Then
'do for statement here
End If
If this does not work, let me know.

Related

VB.NET How to correctly loop through a result set

I have looked at many different code snippets on this site looking that would show me how to do something that should be fairly simple once I have the knowledge.
I want to query a database table for an array of values and then populate a combobox with those results.
Here is what I have so far:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Form1.machineName.DisplayMember = "Text"
Dim tb As New DataTable
tb.Columns.Add("Text", GetType(String))
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
For Each cmd As String In cmd
'I want to add each value found in the database to "tb.Rows.Add"
'tb.Rows.Add(???)
Next
Form1.machineName.DataSource = tb
cn.Open()
cmd.ExecuteNonQuery()
End Using
cn.Close()
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I proceeded much like you did. I used the Load method of the DataTable. It is not necessary to set the column name and type. The name of the column is taken from the Select statement and the datatype is inferred by ADO.net from the first few records.
Luckily a DataTable can be an Enumerable using the .AsEnumnerable method. Then we can use Linq to get all the values from the MachineName column. Calling .ToArray causes the Linq to execute. If you hold your cursor over names on this line you will see that the datatype is String(). Just what we need to fill a combo box.
Code for a class called DataAccess
Private ConnectionString As String = "Your Connection String"
Public Function GetMachineNames() As String()
Dim tb As New DataTable
Dim SQL As String = "SELECT MachineName from machine;"
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim names = dt.AsEnumerable().Select(Function(x) x.Field(Of String)("MachineName")).ToArray()
Return names
End Function
In the form load you combo box like this.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim DatAcc As New DataAccess()
Dim arr = DatAcc.GetMachineNames()
machineName.DataSource = arr
End Sub
If you just want the MachineName to be displayed in the ComboBox, then just use that as the DisplayMember; don't bother creating another column called Text.
Public Sub getMachines()
Try
Dim cmd As String = "SELECT MachineName from machine"
Dim ds As New DataSet()
Using con As New MySqlConnection(ConnectionString)
Using da As New MySqlDataAdapter(cmd, con)
da.Fill(ds)
With Form1.machineName
.DisplayMember = "MachineName"
.ValueMember = "MachineName"
.DataSource = ds
End With
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I'll show a few examples, including using parameters, since that is important.
First up, a quick translation to run the existing query and loop through the results:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Form1.machineName.Items.Add(rdr("MachineName"))
End While
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
But better practice for a method like this is to isolate data access for the UI. This method should return results to the caller, which can decide what do with them. So I'll show two methods: one to get the data, and the other to loop through it and set up the combobox:
Private Function GetMachines() As DataTable
'No try/catch needed here. Handle it in the UI level, instead
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
For Each item As DataRow in getMachines().Rows
Form1.machineName.Items.Add(item("MachineName"))
Next
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
Or, we can use DataBinding:
Private Function GetMachines() As DataTable
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
Form1.machineName.DisplayMember = "FirstName";
Form1.machineName.ValueMember = "City"
Form1.machineName.DataSource = GetMachines()
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
If you ever want to use a filter, you might do this (notice the overloading):
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
da.Fill(result)
End Using
Return result
End Function
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Return GetMachines("%")
End Function
Query parameters like that are very important, and if you were doing string concatenation to accomplish this kind of thing on your old platform, you were doing very bad things there, too.
Finally, let's get fancy. A lot of the time, you really don't want to load an entire result set into RAM, as is done with a DataTable. That can be bad. Instead, you'd like be able to stream results into memory and only work with one at a time, minimizing RAM use. In these cases, you get to play with a DataReader... but returning a DataReader object from within a Using block (which is important) doesn't work that well. To get around this, we can use functional programming concepts and advanced language features:
Private Iterator Function GetMachines(ByVal machineFilter As String) As IEnumerable(Of String)
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Dim result As String = rdr("MachineName")
Yield Return result
End While
End Using
End Using
Return result
End Function
Private Function GetMachines() As IEnumerable(Of String)
Return GetMachines("%")
End Function

Am I loading datatable twice & update row item with boolean value

I call the following function in a module
Public Function GetExcelData(ByVal ExcelFile As String) As System.Data.DataTable
Then I have the following code
If openFileDialog1.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
gblCompName = openFileDialog1.FileName
End If
Dim reader As New DataTableReader(GetExcelData(gblCompName))
Dim table As New DataTable
table.Load(reader)
table.Columns.Add("Single", GetType(Boolean), False)
table.Columns.Add("CouplesInFinal", GetType(Int32))
table.Columns.Add("EvtNum", GetType(String))
table.Columns.Add("EvtStruct", GetType(Int32))
table.Columns.Add("EvtCplID", GetType(Int32))
table.Columns.Add("CouplesInClass", GetType(Int32))
table.Columns.Add("Valid", GetType(Boolean), True)
Dim result() As DataRow = table.Select("[class]" Like "Single")
For Each row In result
If row.Item("Class") Like "Single" Then
table.Rows(0)("Single") = True
End If
Next
DataGridView1.DataSource = table
My logic tells me I'm loading the table twice & the data row field "Single" is boolean which I'm trying to update to True if string field "class" is like "Single"
I realize it's 2 questions in one but it seems to load quite slowly & it's all one form procedure. Any advice on this will be very welcome thanks
Dim reader As New DataTableReader(GetExcelData(gblCompName))
Dim table As New DataTable
Without seeing GetExcelData it is hard to say if you are filling twice. If this function returns a populated DataTable then
Dim table as DataTable = GetExcelData(gblCompName)
and delete the table.Load and the DataReader.
Here is my version of updating a DataTable against my sample database.
Private Sub TestDataTableUpdate()
Try
Using cmd As New SqlCommand("Select * From Coffees", CoffeeCn)
Dim dt As New DataTable
CoffeeCn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader
dt.Load(dr)
End Using
CoffeeCn.Close()
For index As Integer = 0 To dt.Rows.Count - 1
If dt.Rows(index)("Name").ToString = "Cinnamon Stick" Then
dt.Rows(index)("Roast") = "N/A"
End If
Next
'The above does not update the database
Dim da As New SqlDataAdapter(cmd) 'provides the connection and Select command
Dim sqlCB As New SqlCommandBuilder(da) 'Associates the DataAdapter to the command builder
sqlCB.GetUpdateCommand() 'Retrieve the update command for the DataAdapter
'Note: the DataAdapter opens and closes the connection for you
da.Update(dt) 'This updates the database by finding the changed rows in the datatable
'and running the Update command
dt.AcceptChanges() 'Reset the row status to unchanged
DataGridView1.DataSource = dt
End Using
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
CoffeeCn.Close()
End Try
End Sub
PS. I am self-taught also.
The table update method that I got to work is
Dim i As Integer = 0
For i = 0 To table.Rows.Count - 1
If table.Rows(i)("Class") Like "*Single*" Then
table.Rows(i)("Single") = True
End If
Next
I took out the ",False" on this line table.Columns.Add("Single", GetType(Boolean)

Retrieve data from database in asp.net using vb

Below is the code for fetching the data into textbox but its not working it shows error no data exits row/column whereas data and datafield are perfectly alright.
Please help.
Dim Connection As OleDbConnection
Connection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("/db/QardanHasana.mdb"))
Connection.Open()
Dim ejamaatregcmd As OleDbCommand
Dim ejamaatregdtrdr As OleDbDataReader
ejamaatregcmd = New OleDbCommand("SELECT ITSData.[EJamaatID], ITSData.[ITSFirstName] FROM ITSData WHERE EjamaatID= #EjamaatID", Connection)
ejamaatregcmd.Parameters.Add(New OleDbParameter("#EjamaatID", txtEjamaatID.Text))
ejamaatregdtrdr = ejamaatregcmd.ExecuteReader()
If ejamaatregdtrdr.HasRows Then
txtFirstName.Text = ejamaatregdtrdr.item("ITSFirstName").ToString()
end if
A DataReader needs a call to Read to position itself on the first record retrieved
ejamaatregdtrdr = ejamaatregcmd.ExecuteReader()
If ejamaatregdtrdr.HasRows Then
ejamaatregdtrdr.Read()
txtFirstName.Text = ejamaatregdtrdr.item("ITSFirstName").ToString()
End if
By the way, Read returns false if there are no rows to read, so you could remove the test for HasRows and write simply
ejamaatregdtrdr = ejamaatregcmd.ExecuteReader()
If ejamaatregdtrdr.Read() Then
txtFirstName.Text = ejamaatregdtrdr.item("ITSFirstName").ToString()
End if
Another suggestion to improve your code is to start using the Using Statement
Using Connection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;....")
Using ejamaatregcmd = new OleDbCommand("SELECT ITSData.[EJamaatID], ITSData.[ITSFirstName] FROM ITSData WHERE EjamaatID= #EjamaatID", Connection)
Connection.Open()
ejamaatregcmd.Parameters.Add(New OleDbParameter("#EjamaatID", txtEjamaatID.Text))
Using ejamaatregdtrdr = ejamaatregcmd.ExecuteReader()
If ejamaatregdtrdr.Read() Then
txtFirstName.Text = ejamaatregdtrdr.item("ITSFirstName").ToString()
End if
End Using
End Using
End Using
The using statement is invaluable to help you to close and dispose the disposable objects like the connection, the command and the reader. Lacking a proper dispose your code uses more memory and locks resources resulting in a more unstable application.
Before reading data from a DataReader, you need to move the row to the first row by calling the Read method. It returns true if data exist, so you don't need to check the HasRows property:
ejamaatregdtrdr = ejamaatregcmd.ExecuteReader()
If ejamaatregdtrdr.Read() Then
txtFirstName.Text = ejamaatregdtrdr.Item("ITSFirstName").ToString()
End if
I'll generally do something like the following
Function GetResult(ID As string) As String
GetResult = "No Data" 'Default Result
Dim conn As System.Data.OleDb.OleDbConnection = *NewConnectionObject*
Dim comm As System.Data.OleDb.OleDbCommand = *NewCommmandObject*
comm.Parameter.AddWithValue("#Parametername",ID)
Using reader As System.Data.OleDb.OleDbDataReader = comm.ExecuteReader()
If reader.HasRows Then
'Reader has data, so iterate through it
GetResult = ""
while reader.read
GetResult += reader("FirstName")
End While
Else
'Either Do Nothing - Default Result will show
'Throw New System.Exception("Empty Data") 'Try Catch Statement have overhead.. so it's not a popular methodology
'Or Log Something..
End If
End Using
If conn.state = connection.open Then
conn.close
End
End Function

I get only column headers using sqldatareader and datagridview

I have a SQL Server 2008 express with a database and a table and using VB 2010 express.
I am trying to read from that table with sqldatareader, but I only one row in the datagridview with the column headers, no row with data.
What am I doing wrong? (I'm a newbe).
The connection string is :
Data Source=xxxxxxxxxx\SQLEXPRESS;Initial Catalog=Masteca_Inventory;Integrated Security=True
Dim searchStr As String = ""
Dim connetionString As String
Dim sqlCnn As SqlConnection
Dim sqlCmd As SqlCommand
Dim sqlStr As String
Public bindingSource1 As New BindingSource()
connetionString = My.Settings.SQLconnSTR
sqlStr = "SELECT * FROM Piese WHERE " & searchStr 'searchStr is OK I fill it elsewhere
sqlCnn = New SqlConnection(connetionString)
Try
sqlCnn.Open()
sqlCmd = New SqlCommand(sqlStr, sqlCnn)
Dim sqlReader As SqlDataReader = sqlCmd.ExecuteReader()
Using sqlReader
Dim dTable As New DataTable
dTable.Load(sqlReader)
bindingSource1.DataSource = dTable
End Using
SearchReport.DataGridView1.DataSource = bindingSource1
'SearchReport is another form
sqlReader.Close()
sqlCmd.Dispose()
sqlCnn.Close()
SearchReport.Show()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
You are not reading the data as a group (you are fetching only one result).
You will need to adjust the code to use a While sqlReader.Read;
Example;
Dim sqlReader As SqlDataReader = sqlCmd.ExecuteReader()
While sqlReader.Read()
Try
'Do the work needed
rowResult += sqlReader(0) 'This will contain the result script
Catch ex As Exception
'Catch exception
End Try
End While
Something like that should work (I have not tested the code but the concept is the same).
PS - I strongly suggest you adjust your script to add a Where clause and / or the columns needed (Select * is not a "good practice")
Hope this helps.

Better way to print out rows from a datatable in vb.net

I am new to vb.net and I am trying to query a database and print out the records in the row to the console window. I got it to work, but I have a feeling that there is a more concise way to do this. One thing that I am sure is wrong is that I had to convert the dataset to a datatable to be able to retrieve the values. Is that correct? Could you take a look at the code below (especially the for loop) and let me know what I can improve upon?
Thanks!
Module Module1
Sub Main()
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Dim conn As New SqlCeConnection(constring)
Dim cmd As New SqlCeCommand("SELECT * FROM ACCOUNT")
Dim adapter As New SqlCeDataAdapter
Dim ds As New DataSet()
Try
conn.Open()
cmd.Connection = conn
adapter.SelectCommand = cmd
adapter.Fill(ds, "testds")
cmd.Dispose()
adapter.Dispose()
conn.Close()
Dim dt As DataTable = ds.Tables.Item("testds")
Dim row As DataRow
Dim count As Integer = dt.Columns.Count()
For Each row In dt.Rows
Dim i As Integer = 0
While i <= count - 1
Console.Write(row(i))
i += 1
End While
Console.WriteLine(Environment.NewLine())
Next
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
End Module
Here is how I would rewrite this for a few reasons:
1) You should always use Using statements with disposable objects to ensure they are correctly cleaned up. You had a good start with the dispose commands, but this way is safer.
2) It is more efficient to use ExecuteReader than loading everything into a dataset.
3) Your try/catch statement should include object creation as well as execution.
Finally, in response to your question about datasets and datatables, that code was absolutely correct: a dataset consists of zero or more datatables, so you were just extracting the existing datatable from the dataset.
Try
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Using conn As New SqlCeConnection(constring)
conn.Open()
Using cmd As New SqlCeCommand("SELECT * FROM ACCOUNT", conn)
Dim reader As SqlCeDataReader
reader = cmd.ExecuteReader()
Do While reader.Read
For i As Integer = 0 To reader.FieldCount - 1
Console.Write(reader.GetString(i))
Next
Console.WriteLine(Environment.NewLine())
Loop
End Using
End Using
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
One last note: since you are just printing to the console, it doesn't matter as much, but whenever you deal with a lot of strings, especially those that are to be concatenated, you should always consider using System.Text.StringBuilder.
Here is an example rewrite of the loop that prints to the console using stringbuilder (builds the string in memory, then dumps it to the console; I have also added the field name for good measure):
Dim sbOutput As New System.Text.StringBuilder(500)
For i As Integer = 0 To reader.FieldCount - 1
If sbOutput.Length <> 0 Then
sbOutput.Append("; ")
End If
sbOutput.Append(reader.GetName(i)).Append("=").Append(reader.GetString(i))
Next
sbOutput.AppendLine()
Console.Write(sbOutput.ToString)