2

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?

Community
  • 1
  • 1
Keith
  • 1,331
  • 2
  • 13
  • 18
  • Does Order = Head_Order_Number ? – Jiminion May 14 '14 at 16:24
  • Yes, order = head_order_number – Keith May 14 '14 at 16:25
  • you need to do a join. – Jiminion May 14 '14 at 16:29
  • I can't do an SQL join unless there's a way to do an SQL query to two _different_ servers. – Keith May 14 '14 at 17:38
  • Can you load one of the tables into the other database and then do the join? – Jiminion May 14 '14 at 17:58
  • Nope. I would've already done that if I had access to it. Unfortunately, I have to find a way to merge these tables. You'd think it wouldn't be too difficult, but I'm struggling. – Keith May 14 '14 at 18:07
  • 1
    http://nathan.rambeck.org/blog/2-joining-mysql-tables-across-multiple-databases – Jiminion May 14 '14 at 18:40
  • 1
    Very difficult in ADO.Net - simple in Access... As Jim suggested getting local copies of the data is probably the way to go. Create two queries each returning just the relevant data. Or get one set and use it to build queries into the other using an IN() clause with the "join" values. – rheitzman May 14 '14 at 20:52
  • If people are going to downvote, you could at least have the courtesy to explain why. I put forward effort. I explained my problem. I explained my goal. Etc. It'd potentially help me ask/answer better in the future. – Keith May 16 '14 at 15:09
  • 1
    @Jim I know this is late, but thank you for that. It solved a new problem. – Keith Dec 10 '14 at 13:19

1 Answers1

4

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.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
  • 1
    I edited my answer - I'm assuming an OleDb Connection - But a SQL connection is really the same (replace `OleDb` with `Sql`) – Phantômaxx May 15 '14 at 11:41
  • Thank you. I deleted my comment to attempt it on my own for bit, but yes I'm new and definitely lacking knowledge. Thank you again, very much. Trying it now. Edit: Actually, I'm stuck on the connection string part. I'm not sure what to put there as I'm not actually connecting to an actual DB. I'm also using SQL, but that's easily dealt with. – Keith May 15 '14 at 11:42
  • Yep, not sure how to set it up. When I run the program, I get "The ConnectionString property has not been initialized." I agree of course, just not sure where to connect to as it's a datatable source in my program and not a server. – Keith May 15 '14 at 11:59
  • 1
    Even if you are connecting to Access, you have to provide a ConnectionString, where you pass the database name and path (and eventually, user id and password) – Phantômaxx May 15 '14 at 12:32
  • Yea, that's what I figured. The problem is that I filled these two data tables from completely different servers, not just different tables/views on one server. So you put "assuming you have your DB connection set up.." in your post, but that's the problem. There's nothing to connect to. I already have my data in my two tables. I'm unaware of any sort of SQL join-type statement I could use that's cross-server. – Keith May 15 '14 at 12:40
  • 1
    I see. Don't worry. There's a solution - DataRelation, updating my answer ASAP – Phantômaxx May 15 '14 at 12:51
  • 1
    You've been incredibly helpful, but I've encountered an issue with that method, similar to what I encountered the first time I tried DataRelations. I've updated my Question with code at the bottom. I might be doing something wrong, but the error it's giving me is: "System.ArgumentException: This constraint cannot be enabled as not all values have corresponding parent values." I really appreciate everything so far by the way. – Keith May 15 '14 at 16:16
  • Try adding `, False` to `Dim dr As DataRelation = New DataRelation("Combined", _ ds.Tables("DataTable1").Columns("OrderNo"), _ ds.Tables("DataTable2").Columns("OrderNo"))` so to read `Dim dr As DataRelation = New DataRelation("Combined", _ ds.Tables("DataTable1").Columns("OrderNo"), _ ds.Tables("DataTable2").Columns("OrderNo"), False)` – Phantômaxx May 15 '14 at 16:42
  • I tried swapping the two tables (DataTable1 and DataTable2) around in the DataRelation, it then gave me the error: "These columns don't currently have unique values". ----- I then tried setting the last function parameter to false as you suggested. This works as it gives no error, but for some reason, "dgvDataGrid.DataSource = ds" doesn't seem to be working. My grid is blank. I know the data is there. I tested the tables independently. When debugging to that line, I noticed there the grid's rows and columns are 0, so the DataSet is not passing anything to it? – Keith May 15 '14 at 17:26
  • 1
    The parent column has to contain unique values. It is also possible that parent column contains multiple rows with NULL value or blank string in your case. – Phantômaxx May 15 '14 at 18:41