I am a little new to using vb.net and SQL so I figured I would check with you guys to see if what I am doing makes sense, or if there is a better way. For the first step I need to read in all the rows from a couple of tables and store the data in the way the code needs to see it. First I get a count:
mysqlCommand = New SQLCommand("SELECT COUNT(*) From TableName")
Try
SQLConnection.Open()
count = myCommand.ExecuteScalar()
Catch ex As SqlException
Finally
SQLConnection.Close()
End Try
Next
Now I just want to iterate through the rows, but I am having a hard time with two parts, First, I cannot figure out the SELECT statement that will jet me grab a particular row of the table. I saw the example here, How to select the nth row in a SQL database table?. However, this was how to do it in SQL only, but I was not sure how well that would translate over to a vb.net call.
Second, in the above mycommand.ExecuteScalar() tell VB that we expect a number back from this. I believe the select statement will return a DataRow, but I do not know which Execute() statement tells the script to expect that.
Thank you in advance.
A simple approach is using a DataTable which you iterate row by row. You can use a DataAdapter to fill it. Use the Using-statement to dispose/close objects property that implement IDisposable like the connection:
Dim table = New DataTable
Using sqlConnection = New SqlConnection("ConnectionString")
Using da = New SqlDataAdapter("SELECT Column1, Column2, ColumnX FROM TableName ORDER By Column1", sqlConnection)
' you dont need to open/close the connection with a DataAdapter '
da.Fill(table)
End Using
End Using
Now you can iterate all rows with a loop:
For Each row As DataRow In table.Rows
Dim col1 As Int32 = row.Field(Of Int32)(0)
Dim col2 As String = row.Field(Of String)("Column1")
' ...'
Next
or use the table as DataSource for a databound control.
Related
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
Simple question:
When I call SqlDataAdapter.Fill(DataGridView.DataSource) the second time after initially creating first Data it does not update the contained rows. It simply adds all rows returned by the select command to my DataGridView.
If I call it a third, fourth (so on) it will also just add the returned rows.
Am I understanding the .Fill(DataTable) function wrong? How do I update the already existing DataTable correctly? Which line of code is responsible for that?
Turns out it has to be a code problem;
DataGridView1.AutoGenerateColumns = False
Dim sql = "select * from myTable"
oDtSource = New DataTable
oAdapter = New SqlDataAdapter
oCon = sqlCon("serverName\Instance", "myDataBase") ' Returns a SqlConnection
oCmd = New SqlCommand(sql, oCon)
oCon.Open()
oDtSource.Clear()
oAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
oAdapter.SelectCommand = oCmd
oAdapter.Fill(oDtSource)
DataGridView1.DataSource = oDtSource
For refreshing I use oAdapter.Fill(oDtSource)
The PrimaryKey is set in the database
From MSDN:
You can use the Fill method multiple times on the same DataTable. If a
primary key exists, incoming rows are merged with matching rows that
already exist. If no primary key exists, incoming rows are appended to
the DataTable.
So either define a primary key or clear the table first.
Dim table = CType(DataGridView.DataSource, DataTable)
table.Clear()
' fill ...
To define primary key(s) manually read this. To let it create automatically if they are defined in the database you need to set the MissingSchemaAction to AddWithKey:
' ...
dataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
' fill ...
The edit code doesnt show the PrimaryKey being defined for the DataTable. This will configure the DataAdapter to perform updates and enabled refreshing the DataTable. The code uses MySQL but the Provider objects all work the same in this regard:
' persistant form level vars
Private daSample As MySqlDataAdapter
Private dtSample As DataTable
...
Elsewhere:
' there are caveats with WHERE clauses
Dim sql = "SELECT Id, Name, Country, Animal FROM SAMPLE WHERE Color = 'onyx'"
' using the ctor overload, no need for a DbCommand or Connection object
daSample = New MySqlDataAdapter(sql, MySQLConnStr)
' initialize the CommandBuilder, get other commands
Dim cbSample = New MySqlCommandBuilder(daSample)
daSample.UpdateCommand = cbSample.GetUpdateCommand
daSample.InsertCommand = cbSample.GetInsertCommand
daSample.DeleteCommand = cbSample.GetDeleteCommand
dtSample = New DataTable()
daSample.FillSchema(dtSample, SchemaType.Source)
dtSample.PrimaryKey = New DataColumn() {dtSample.Columns("id")}
daSample.Fill(dtSample)
dgv1.DataSource = dtSample
To pick up changes made to the db from other client apps:
daSample.Fill(dtSample)
Initial display:
After I change a row to "onyx" from a UI browser and Update the changed row shows up:
WHERE clauses can be a bit of an issue. Since it restricts the subset of data pulled back, Update is only going to compare rows in the new result set. So, if I change an onlyx row to "blue" it wont be removed.
One solution is to use .DefaultView.RowFilter on the table, but that can slow things down since it requires returning all rows to the client to be filtered there. Its not perfect.
I have a situation where I want to Insert into access DB table from MS SQL table.
Same columns and everything.
I have both data sets and both table adapter. I can do what ever I want inside each dataset - any manipulation but I cannot insert from one table to another.
I tried creating an Insert query for destination tableadapter but I cannot get the from working. Tried linking, nothing works.
Searched for days, simply cannot find it.
Thank you for your answer. Can you help me on my example. I'm having trouble setting this up. This is what i got:
Dim myToTableTableAdapter As FirstDataSetTableAdapters.ToTableTableAdapter
myToTableTableAdapter = New FirstDataSetTableAdapters.ToTableTableAdapter()
Dim myFromTableTableAdapter As SecondDataSetTableAdapters.FromTableTableAdapter
myFromTableTableAdapter = New SecondDataSetTableAdapters.FromTableTableAdapter()
myFromTableTableAdapter = myToTableTableAdapter.Clone
'but it doesnt work from here`
What I wanted to do is:
For each drfrom As DataRow In myFromTableTableAdapter.GetData
myToTableTableAdapter.InsertInto(drfrom.item(column01), drfrom.item(column02), drfrom.item(andSoOn))
Next
But it seem to me that this would take so much longer then a "Insert Into From Select" script.
You cannot insert a row from one table into another table, but there are a couple of ways to do what you want. One way (a little verbose) is this:
' sets it up with same schema but empty rows
mOutTable = inTable.Clone
' Now insert the rows:
For Each rowIn In inTable.Rows
r = mOutTable.NewRow()
For Each col In inTable.Columns
r(col.ColumnName) = rowIn(col.ColumnName)
Next
mOutTable.Rows.Add(r)
Next
mOutTable.AcceptChanges
A second way, which is more concise, is this:
outTable = inTable.Clone
For Each inRow As DataRow In inTable.Rows
outTable.LoadDataRow(inRow.ItemArray, False)
End If
outTable.AcceptChanges
Note that both inTable and outTable are ADO.NET DataTable objects. You cannot implement my suggestion on the DataAdapter objects. You must use the DataTable objects. Each DataTable can be associated with a DataAdapter in the standard fashion for ADO.NET:
Dim t as New DataTable()
a.Fill(t);
where a is the ADO.NET DataAdapter. I hope this helps!
Jim
I need to read row by row in a column in a table then I need to store this then call procedure to insert data to a different column using vb.net.
I have already create the DB connection and I know how to call the procedure
but I'm not sure of how to read in the loop and then to assign it to a variable to call it in the store procedure.
Dim drDocs As SqlClient.SqlDataReader
Dim cmdDocs As SqlClient.SqlCommand
Dim Doc As Long
Dim l As Long
Using conn As New SqlConnection(DBpath)
cmdDocs = New SqlClient.SqlCommand("Select (RecordID) from DocID", conn)
drDocs = cmdDocs.ExecuteReader
Do While drDocs.Read
'need it read each row in that field and hold value'
Loop
drDocs.Close()
cmdDocs.Dispose()
If Doc Then
cmdDocs = New SqlClient.SqlCommand("Insert_Doc", conn)
cmdDocs.CommandType = CommandType.StoredProcedure
cmdDocs.Parameters.Add("path", SqlDbType.NVarChar).Value =need to put hold value from reading that cloumn row by row
End If
End If
The code you've provided actually works now. It is as Juergen D says, sql functions like Max(), min() and using Limit will only return 1/certain number of rows based on their conditions.
if I may, just use this SQL command
"select `RecordID` from DocID asc;"
If you want it in descending format, use desc instead
...now reading further, I realize that what you want to do is to store the results, then loop again through it so that you can do an sql command with it, correct? what you can do then is to pass the SQL results to a container (I use datagridviews) then loop through the container.
I'm currently pulling information using a query that I'm not allowed to tamper with:
Dim dt As DataTable = BLL.GetData(variable).Tables(0)
Immediately afterwards, I'm removing any records where a field begins with a specific value:
For Each dr As DataRow In dt.Rows
If dr.Item(2).ToString().StartsWith("value") Then
dr.Delete()
End If
Next
What I'd really like to do is something like:
dt.Select.Where(field1 => field1.StartsWith("value")).Delete()
I know that is not the syntax of it and I'm probably very off from what it would be like. The For Each works fine, I'm just trying to "simplify" it. Any idea? Any and all help is appreciated.
Actually, your initial code is probably the cleanest and most straight forward.
To delete items using LINQ, you first need to read them into a separate collection, then loop through that collection and call Delete on each record. If you'd rather go that route, you could try:
Dim records = dt.Rows.Where(Function(r) r.StartsWith("value")).ToList()
For Each r In records
r.Delete()
Next
The answer I think you are looking for is below from Microsoft. https://msdn.microsoft.com/en-us/library/det4aw50(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2
Dim table As DataTable = DataSet1.Tables("Orders")
' Presuming the DataTable has a column named Date.
Dim expression As String
expression = "Date > #1/1/00#"
Dim foundRows() As DataRow
' Use the Select method to find all rows matching the filter.
foundRows = table.Select(expression)
Dim i As Integer
' Print column 0 of each returned row.
For i = 0 to foundRows.GetUpperBound(0)
Console.WriteLine(foundRows(i)(0))
Next i