0

Using this code, I created a datatable out of an Excel document. The data set is also being displayed in a datagridview.

                OleDbcon.Open();                  
                tbContainer = new SD.DataTable();
                OleDbCommand cmd = new OleDbCommand();
                System.Data.DataTable dt = new System.Data.DataTable();
                OleDbDataAdapter oda = new OleDbDataAdapter(string.Format("select * from [{0}$]", sheetName), OleDbcon);
                DataSet ds = new DataSet();
                oda.Fill(ds);
                DGVExcel.DataSource = ds.Tables[0];
                Console.WriteLine(dt.Rows.Count);
                OleDbcon.Close();

Now that I have this Dataset/DataTable, I want to be able to match its data with the contents of an already established access database and then add relevant values.

Say for example I have this in the database:

Name || Fingers || Thumbs || ID

Ross || 10 || 2 || 12

Boss || 9 || 1 || 14

The excel file happens to have the same exact data and the fields will be added except for the ID, which is used to identify the unique entries.

So Ross' value becomes 20||4, Boss becomes 18 || 2.

How do I make use of the DataSet/DataTable I just made to perform this operation?

The ideas I thought of would be to use an 'if not exists' Select * from Table where ID = [?]. But I don't know how to set the ID. Would a for loop work? Pseudocode:

for (int i = 2; i < DB.Count ; i++){
  string query = "if not exists Select * from Table where ID = " + DGridView.Rows[i].Cells[LastCell];

I was recommended Linq before but I honestly don't know where to start on that.

EDIT x2:

=======================================

So I think I've been going about this the wrong way, at any rate, I managed to insert my excel data DIRECTLY into the Access table without having to resort to a dataset/raw query. The only caveat is that the table structure needs to match the access table exactly, though I think that was always the case at any rate.

Code:

                    conn.Open();
                    string sqls = @"INSERT INTO ACXL SELECT * FROM [Excel 12.0;HDR=YES;DATABASE=" + openFileDialog.FileName + "].[" + txtSheetName.Text + "$];";
                    OleDbCommand cmd = new OleDbCommand();
                    cmd.Connection = conn;
                    cmd.CommandText = sqls;
                    cmd.ExecuteNonQuery();
                    conn.Close();

On to the SQL table matching!

Avan
  • 223
  • 3
  • 13
  • 1
    Based on your description, I believe you can infer a natural key of `Name`, so just change your if not exists select to use that instead of ID. – John Wu Dec 01 '16 at 08:04
  • 1
    one of your best ways of doing this would be to pass it in to a SQL Stored Procedure that completes a merge statement that can insert, update and delete (if you want to) any record in the table that matches your conditions – Simon Price Dec 01 '16 at 08:19
  • 1
    @JohnWu, I'm not sure if that would change much at all. The problem i'm having is how to keep the index relative to the Table and Databases content. So, it could be any number of ID's or Names for that number, I'm guessing a name might have duplicate values, therefore the ID field is what I'm using for reference. – Avan Dec 01 '16 at 08:21
  • 1
    @SimonPrice, I am working with an MS Access database. I do not think it is capable of using stored procedures? I've worked with SQL triggers before, is this the same principle? – Avan Dec 01 '16 at 08:24
  • 1
    oh, my bad... sorry thought you were working with a SQL Database, ok then LINQ is your best friend in this instance and would recommend you buy and read this https://www.microsoftpressstore.com/store/programming-microsoft-linq-in-.net-framework-4-9780735640566 – Simon Price Dec 01 '16 at 08:28
  • 1
    If you can't use `Name` to match rows, how do you know "Ross' value becomes 20||4" .. ? – John Wu Dec 01 '16 at 08:31
  • 1
    Well I don't, not explicitly at least. All I know is that the ID is incorruptible, and determines the query start, that the name is ignored somehow, and the rest of the fields are computed against the copy. Honestly, this is all just theory and I really have no idea what the ideal situation should be. @JohnWu – Avan Dec 01 '16 at 08:35
  • If you can't articulate the requirements and can't manually match a single record in your head, how do you expect to program a computer to do it? Unless of course you are asking for advice about AI. – John Wu Dec 01 '16 at 09:04
  • 1
    @JohnWu, That was sudden. What I meant was that the names Ross and Bob are not declared explicitly for the purposes of the program and are effectively N, a value ignored by the program. The variable I represents the ID field which is unique and reference-able. If I matches the copy of I in the database then the computable fields are added. Since the name field already exists, there is no need to move it, especially since there could be duplicates of any given name the ID field is preferred. Though, I believe I've already stated all of this earlier. – Avan Dec 01 '16 at 09:18
  • OK... so the Excel sheet does have the ID? I thought you said it had all the fields "except for the ID." Is every ID in the Excel sheet also present in the Access database, or do you need to check for existence and insert if missing? – John Wu Dec 01 '16 at 10:25
  • 1
    @JohnWu, OH, damn I didn't know I wrote it that way. Okay, i'll start from the beginning so I don't confuse myself. The Access database is built with an ID field which is the primary key, this auto-increments. I may have accidentally left this one out. Next, string name field and int finger and thumb fields. Lastly I have a Person ID field which was the ID field I was talking about, which is indexed as unique/no duplicates. To clarify, yes every row/ID _could_ be included in the Excel file but it may be inserted by piece meal so I would stillneedtocheck. NameStringintintint is the Excel forma – Avan Dec 01 '16 at 13:46

1 Answers1

2

Here's how I would do it.

  1. Create a "working table" in Access that contains the same column definitions as the main table, except the ID column should be an int and not an identity, so that you can manually insert values into it.

  2. Insert rows into the working table, one row per row in the Excel spreadsheet. You can do this by iterating over the data set using for...each and executing an INSERT statement for each row.

  3. Execute a single UPDATE statement that joins the working table to the main table and sums the fields as required. By performing this action in a single statement you avoid the sticky situation that would occur if you were doing the updates row by row and something failed partway through.

  4. Truncate the working table.

To get the rows inserted into the temp table, I'd probably write something like this. Note that you don't need to pass the Name since it is completely dependent on ID anyway. This simplifies things-- if you passed it, you'd have to escape it too.

void InsertRowIntoTempTable(int id, int fingers, int thumbs)
{
    using (var conn = this.GetDatabaseConnection())
    {
        var cmd = new SqlCommand(conn);
        cmd.CommandText = String.Format("INSERT INTO TempTable VALUES ({0},{1},{2})", id, fingers, thumbs);
        conn.Open();
        cmd.ExecuteNonQuery();
    }
}

void InsertRowsIntoTempTable(DataTable source)
{
    for each (var row in source.Rows)
    {
        InsertRowIntoTempTable(row["ID"], row["Fingers"], row["Thumbs"]);
    }
}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • 1
    I'm gonna be trying to produce a working sample soon. So, what I want is an inner join right? Since I don't really want these null values and it only has to add the ones that do match. Something like `Update ACTB VALUES Fingers, Thumbs from Excel INNER JOIN ACTB on Excel.PersonID == ACTB.PersonID` `select Excel.*,ACTB.* from Excel,ACTB where Excel.PersonID = ACTB.PersonID;` – Avan Dec 02 '16 at 02:46
  • Follow [this pattern](http://stackoverflow.com/questions/12737221/update-msaccess-table-from-another-access-table-using-sql). – John Wu Dec 02 '16 at 04:31
  • 1
    I tried your Edit but it's throwing an error: Cannot convert type object to int from your foreach (var row in source.Rows). Or type object is not assignable to type 'int'. In the meantime I'll try to get the foreach's above to work. – Avan Dec 05 '16 at 04:34
  • 1
    I managed to simply insert the Excel data directly into the Access database without the Dataset/table middleman. See my edit on the code, but it's abit too simple. I will proceed to create a query prototype. – Avan Dec 05 '16 at 09:23