How to inner join two already filled DataTables in VB.NET - vb.net

I marked Der Golem's answer as correct as it was the right answer for the specific problem I said I was having.
Unfortunately, I failed to detect the fact that the records in datatable2 aren't unique, which obviously doesn't jive well with performing an "inner join" on the two tables for the result I was wanting.
Setting a DataRelation would work if it was.
I'm still new to VB.NET, so please bear with me.
I have two data tables, each filled from different database servers.
They both have three columns (for ease of testing, final program will have 50+).
They both have a common data column that I want to inner join by ("OrderNum").
There's a question here, but the "answer" doesn't work for me, nor the LINQ option below it:
Merging 2 data tables in vb.net
This is my example code:
DB1 = New DatabaseConnectionSQL1
DB1.OpenConn()
DB2 = New DB_DatabaseConnectionSQL2
DB2.OpenConn()
Dim dtA As DataTable = New DataTable("DataTable1")
Dim dtB As DataTable = New DataTable("DataTable2")
Dim dtCombined As DataTable = New DataTable("CombinedDataTable")
dtA.Columns.Add("Order", System.Type.GetType("System.String"))
dtA.Columns.Add("Account_Number", System.Type.GetType("System.String"))
dtA.Columns.Add("Account_Name", System.Type.GetType("System.String"))
'"Order" = "Head_Order_Number"
dtB.Columns.Add("Head_Order_Number", System.Type.GetType("System.String"))
dtB.Columns.Add("Line_Number", System.Type.GetType("System.Int32"))
dtB.Columns.Add("Model", System.Type.GetType("System.String"))
dtA = DB1.GetDataTable(sQuery1)
dtB = DB2.GetDataTable(sQuery2)
'This doesn't work as it just appends the table
'dtA.Merge(dtB, True)
'I tried creating a DataSet and setting a Relation, but that kept failing
'I've tried at least 10 different things here. I'm at my wit's end.
dgvDataGrid.DataSource = dtCombined
dgvDataGrid.Refresh()
DB1.CloseConn()
DB2.CloseConn()
I noticed people in other places are suggesting using Linq. Even though I'm not familiar with it, I tried my best and kept failing.
Table A (dtA):
Order | Account_Number | Account_Name
10000 | 10000000000001 | BlahA
20000 | 10000000000002 | BlahB
30000 | 10000000000003 | BlahC
Table B (dtB):
Head_Order_Number| Line_Number | Model
10000 | 00000000034 | MD35Z
15000 | 00000000530 | MX25A
25000 | 00000024535 | P231Y
20000 | 00000027735 | A511L
30000 | 00000000910 | M232C
Final table I want combining the two (dtCombined):
Order | Account_Number | Account_Name | Line_Number | Model
10000 | 10000000000001 | BlahA | 00000000034 | MD35Z
20000 | 10000000000002 | BlahB | 00000027735 | A511L
30000 | 10000000000003 | BlahC | 00000000910 | M232C
Any help would be greatly appreciated.
I tried adding a DataRelation before and kept getting an error, but I wasn't setting something up properly. Now that I fixed that problem, I'm getting another error:
"System.ArgumentException: This constraint cannot be enabled as not all values have corresponding parent values."
dt1 = New DataTable("DataTable1")
dt1.Columns.Add("order_number", System.Type.GetType("System.String"))
dt1.Columns.Add("account_name", System.Type.GetType("System.String"))
dt2 = New DataTable("DataTable2")
dt2.Columns.Add("head_order_number", System.Type.GetType("System.String"))
dt2.Columns.Add("model", System.Type.GetType("System.String"))
Conn1.ConnectionString = sConnString1
Dim da1 As SqlDataAdapter = New SqlDataAdapter(sQuery1, Conn1)
Conn1.Open()
Conn2.ConnectionString = sConnString2
Dim da2 As SqlDataAdapter = New SqlDataAdapter(sQuery2, Conn2)
Conn2.Open()
ds = New DataSet
da1.Fill(ds, "DataTable1")
da2.Fill(ds, "DataTable2")
Dim dr As DataRelation = New DataRelation("Combined", _
ds.Tables("DataTable1").Columns("OrderNo"), _
ds.Tables("DataTable2").Columns("OrderNo"))
ds.Relations.Add(dr)
dgvDataGrid.DataSource = ds
dgvDataGrid.Refresh()
Conn1.Close()
Conn2.Close()
That error seems to make sense, as DataTable1 has 1950 total rows, while DataTable2 has over 4000, but isn't that the point of the DataRelation? It effectively inner joins the two tables so the end result should be 1950 rows?

The query you want to execute looks like this one:
Dim sql As String = "SELECT dta.*, dtB.* FROM dtA INNER JOIN dtB ON dtA.Order = dtB.Order"
Please note that the record with Order = 25000 is not part of the INNER JOIN
[EDIT]
As per your comment, I see you lack some knowldge...
So - ASSUMING you already have your db connection prepared (conn):
Dim cmd As OleDbCommand = New OleDbCommand(sql, conn)
Dim da As OleDbDataAdapter = New OleDbDataAdapter
da.SelectCommand = cmd
conn.Open()
Dim ds As DataSet = New DataSet
da.Fill(ds, "Result")
conn.Close()
dgvDataGrid.datasource = ds
ds.DataBind()
I'm assuming an OleDb Connection - But a SQL connection is really the same (replace OleDb with Sql)
[EDIT 2] You decided to make me sweat!
Finally, there's a solution for your very specific problem:
As shown here: http://msdn.microsoft.com/en-us/library/cc188919.aspx
The DataRelation object is what you need.
Creating DataRelation Objects
' Create the DataRelation and
' relate the customers to their orders
DataRelation oDr_Customer2Order = new DataRelation("Customer2Order",
oDs.Tables["Customer"].Columns["CustomerID"],
oDs.Tables["Order"].Columns["CustomerID"]);
oDs.Relations.Add(oDr_Customer2Order);
By creating the DataRelation objects and then adding them to the DataSet's Relations collection, the three DataTable objects' rowsets are related to one another through the defined fields. Like most of the ADO.NET objects, the DataRelation object has several different constructors. I used the constructor that accepts the name of the relation, the parent table's column, and the child table's column. If there were multiple columns that define the relationship, I could have passed in an array of the parent table's columns and an array of the child table's columns. Another option is to use the same first three parameters that I used in Figure 3 and then pass in a fourth parameter to represent whether the constraints should be created automatically (pass in a Boolean value). But more on constraints in a moment.
Once the DataSet is filled with the three rowsets and the relations are established linking the DataTable objects, the DataSet could easily be displayed in a DataGrid on a Web Form by setting the DataSource property like this:
dataGrid1.DataSource = oDs;
The DataGrid is clever enough to figure out that there are multiple DataTable objects that need to be displayed and that it should allow the rowsets to be navigated in the order that's prescribed by the DataRelation objects.

Related

Filtering SQL data into multiple DataTables in a single DataSet

This should be simple, but it's escaping me right now as I have not written in VB in over a decade.
I have a stored procedure that gets a list of students and their assigned homeroom teacher.
Very basic - just the three columns: FirstName, LastName, Teacher.
What I need is to load the data into a DataSet where each Teacher has their own DataTable filled with the students assigned to them.
Sample data:
So in the example above, I would end up with 3 DataTables named Adams, Taggert and Dublin, each populated with the students associated with them all in a single DataSet in such a way that I could call the data with:
For Each dr As DataRow In ds.Tables("Taggert").Rows
' Do something with dr("FirstName").ToString
Next
Any help would be appreciated.
I ultimately used elements from both answers to find a solution. I ended up using DataViews to create the tables, and added them into the new DataSet. It works perfectly. It iterates through every record, but for the few hundred students it handles, it's actually quite
efficient. Thanks for the comments.
For Each row As DataRow In ds.Tables(0).Rows
Dim Teacher As String = row("Teacher").ToString()
If Not dsOutput.Tables.Contains(Teacher) Then
Dim dvStudents = ds.Tables(0).DefaultView
dvStudents.RowFilter = "Teacher = '" & Teacher & "'"
Dim subTable As New DataTable(Teacher)
subTable = dvStudents.ToTable(Teacher)
dsOutput.Tables.Add(subTable)
End If
Next
old_datatable with all teachers , i will declare this but you use your own
Dim old_datatable As New DataTable
Dim new_dataset As New DataSet
For Each row As DataRow In old_datatable.Rows
Dim teacher As String = row("Teacher")
If new_dataset.Tables.Contains(teacher) = False Then
Dim new_datatable As New DataTable
new_dataset.Tables.Add(new_datatable)
End If
new_dataset.Tables(teacher).Rows.Add(row)
Next
For Each dr As DataRow In new_dataset.Tables("Taggert").Rows
' Do something with dr("FirstName").ToString
Next

Creating datatable from 2 datatables created at runtime VB.net

I have a pretty long code which creates two datatables and fills them with data at runtime. And i need to create one table, but not the way merge does it but like adding columns from one datatable as new columns(but filled with data) to the other one. Best example i can thing of is Join in SQL but i will try to draw some example. I can not put my code here as i said it't too long.
Example:
(only column names but imagine that under each |something| is a lot of rows)
Table1:
Date|AA|AA2|AA3|AA4|AA5|AA6|
Table2:
Date|BB|BB2|BB3|BB4|BB5|BB6|
Result:
Date|AA|AA2|AA3|AA4|AA5|AA6|BB|BB2|BB3|BB4|BB5|BB6|
The DataTable.Merge method is perfectly capable of yielding your desired result. As your two tables have only one column in common Date, I am assuming that it is PrimaryKey of at least Table 1.
Dim dt1 As New DataTable
Dim pk As DataColumn = dt1.Columns.Add("Date", GetType(DateTime))
dt1.PrimaryKey = {pk}
dt1.Columns.Add("C1")
dt1.Columns.Add("C2")
dt1.Rows.Add(#1/1/2018#, "c1", "c2")
Dim dt2 As New DataTable
Dim pk2 As DataColumn = dt2.Columns.Add("Date", GetType(DateTime))
dt2.Columns.Add("C3")
dt2.Columns.Add("C4")
dt2.Rows.Add(#1/1/2018#, "c3", "c4")
dt2.Rows.Add(#1/2/2018#, "c3a", "c4a")
Dim dt3 As DataTable = dt1.Copy
dt3.Merge(dt2)
DataGridView1.DataSource = dt3
This code yield this result:

Save all items of Listbox to access Database Table in VB.Net

I am working on a project and have hit a brick wall again. I have a listbox with some math operations in it (i.e. "1+2=3"). I am trying to save all items in the listbox to an access database, and I think I am getting close. The database has two entries I put in manually, but when I use the following code I only get -1's (plus the two manual entries) in the listbox (when I clear the listbox and load the database into the listbox). The other issue is I need to limit the database to 10 rows which I have no idea how to do. What I would like to do is check the database each time I save to it to see how many rows it has. If the number of listbox items I am saving will exceed 10 rows in the database then clear the database and start from 0 rows. Here is the code I have (which only attempts to save to database):
con.ConnectionString = "Provider = Microsoft.ACE.OLEDB.12.0; Data Source = Mathops.accdb; Persist Security Info=False;"
Dim cmd As New OleDbCommand
Dim var1 As String
con.Open()
cmd.Connection = con
Try
For i As Integer = 0 To lstDisplay.Items.Count - 1
var1 = lstDisplay.Items.Item(i).ToString
cmd.CommandText = ("INSERT into Records (Operations) VALUES(" + var1 + ")")
cmd.ExecuteNonQuery()
Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
con.Close()
Thank you for any assistance you can provide!
John
Oh...and the database has 2 fields; ID and Operations where ID is the Primary Key.
Try using listdisplay.items(i) to get the text from your listbox.
Then to sort out the 10 items issue:
If you do an SQL query:
SELECT Max(id) FROM Records that will give you the biggest id value in the table
Then check if the value you get back >= 10, and if it is use the SQL:
DELETE FROM Records to clear the table.

how can I show only the new rows in the datagridview

how can I show only the rows in the gridview,
where primarykey>= primarykey.max - 20 (the 20 latest lines)
When I create a SQL Statement,
can I use a variable which is declared at vb.net?(for example a counter which counts the amount of added rows)
where must I put that statement in?
Use this SQL statement as DataSource
SELECT TOP 20 FROM YourTable ORDER BY PrimaryKey DESC
UPDATE
If table name and PKEY must be dynamic you can use a parameterized command. Once the sql statement is formed you can fill a DataSet and use it as data source when populating the DataGridView
Dim cmd As New SqlCommand("SELECT TOP 20 FROM ? ORDER BY ? DESC", Connection)
cmd.Parameters.AddWithValue("#Table", YourDinamicTableName)
cmd.Parameters.AddWithValue("#PKey", YourDinamicKeyName)
Dim ds As New DataSet
Dim Adapter As New SqlDataAdapter(cmd)
Adapter.Fill(ds)
DataGridView1.DataSource = ds.Tables(0)

Insert To Database Table With Bindingsource / DataAdapter / Dataset (Vb.NET)

Condition :
I have 2 tables HISTORY1 and HISTORY2, those tables has the same columns but different data.
And i'm using these code below to retrieve data from database to bindingsource
dim da =new sqldataadapter
dim ds =new dataset
dim bs =new bindingsource
dim bs2 = new bindingsource
da.SelectCommand = "select * from HISTORY1"
da.fill(ds,"HISTORY1")
bs.datasource=ds.tables("HISTORY1")
And Then i add another table to bs2
da.selectcommand="select*from HISTORY2"
da.fill(ds,"HISTORY2")
bs2.datasource=ds.tables("HISTORY2")
Problem :
Now, i want to copy every single data from HISTORY2 to HISTORY1 table.
Question :
Can i update HISTORY1 from bs2?
Is there any easier way to retrieve data from database to bindingsource?
You should find what you want by searching "DataTable Merge"
here's a link on how to do it : http://vb.net-informations.com/dataset/dataset-merge-tables-sqlserver.htm
How it works :
Fill your dataTable With History1 like usual.
Change your select command to get the same table structure from your second table (History2),
Fill Again, the same dataSet but an other Table.
myDataSet.Tables(0).merge(myDataSet.Tables(1))
TADA!