How to Insert Rows to Table Object Inside an Excel Sheet? - c#

I have difficulties trying to insert rows into an existing table object. Here is my code snippet:
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + #"C:\myExcelFile.xlsx" + ";Extended Properties=\"Excel 12.0;ReadOnly=False;HDR=Yes;\"";
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
string insertQuery = String.Format("Insert into [{0}$] (ID, Title,NTV_DB, Type ) values(7959, 8,'e','Type1')", TabDisplayName);
cmd.CommandText = insertQuery;
cmd.ExecuteNonQuery();
cmd = null;
conn.Close();
}
As a result I get my rows inserted below a ready-made table object:
I've also tried inserting data inside a table object like so:
string insertQuery = String.Format("Insert into [{0}$].[MyTable] (ID, Title,NTV_DB, Type ) values(7959, 8,'e','Type1')", TabDisplayName);
But I get an error:
The Microsoft Access database engine could not find the object 'MyTable'. Make sure the object exists and that you spell its name and the path name correctly. If 'MyTable' is not a local object, check your network connection or contact the server administrator.
As you can see, table with a name MyTable does exist. I would be very grateful if someone can shed some light on this mystery.

If you are using the Microsoft.ACE.OLEDB provider, then be aware that it doesn't support a named range. You need to provide the name of the sheet [Sheet1$] or the name of the sheet followed by the range [Sheet1$A1:P7928].
If the range is not provided, it will then define the table as the used range, which may contains empty rows.
One way to deal with empty rows would be to delete them, but the driver doesn't support the DELETE operation.
Another way is to first count the number of rows with a non empty Id and then use the result to define the range of the table for the INSERT statement:
using (OleDbConnection conn = new OleDbConnection(connectionString)) {
conn.Open();
string SheetName = "Sheet1";
string TableRange = "A1:P{0}";
// count the number of non empty rows
using (var cmd1 = new OleDbCommand(null, conn)) {
cmd1.CommandText = String.Format(
"SELECT COUNT(*) FROM [{0}$] WHERE ID IS NOT NULL;"
, SheetName);
TableRange = string.Format(TableRange, (int)cmd1.ExecuteScalar() + 1);
}
// insert a new record
using (var cmd2 = new OleDbCommand(null, conn)) {
cmd2.CommandText = String.Format(
"INSERT INTO [{0}${1}] (ID, Title, NTV_DB, Type) VALUES(7959, 8,'e','Type1');"
, SheetName, TableRange);
cmd2.ExecuteNonQuery();
}
}

If you execute this code:
var contents = new DataTable();
using (OleDbDataAdapter adapter = new OleDbDataAdapter(string.Format("Select * From [{0}$]", TabDisplayName), conn))
{
adapter.Fill(contents);
}
Console.WriteLine(contents.Rows.Count);//7938
you will see 7938 (last row number on your screenshot). And when you insert new row, it inserted at 7939 position. Empty content in (7929, 7930, ...) rows are ignored, because excel knows that last number is 7938.
Solutions:
You must delete all rows after 7928 in excel file.
You must insert on specific position.

I'm not sure Access C# works the same as Excel, but this worked on a spreadsheet for me. Maybe it could help you?
Table3.ListRows[1].Range.Insert(Excel.XlInsertShiftDirection.xlShiftDown);

Try this
private void GetExcelSheets(string FilePath, string Extension, string isHDR)
{
string conStr="";
switch (Extension)
{
case ".xls": //Excel 97-03
conStr = ConfigurationManager.ConnectionStrings["Excel03ConString"]
.ConnectionString;
break;
case ".xlsx": //Excel 07
conStr = ConfigurationManager.ConnectionStrings["Excel07ConString"]
.ConnectionString;
break;
}
//Get the Sheets in Excel WorkBoo
conStr = String.Format(conStr, FilePath, isHDR);
OleDbConnection connExcel = new OleDbConnection(conStr);
OleDbCommand cmdExcel = new OleDbCommand();
OleDbDataAdapter oda = new OleDbDataAdapter();
cmdExcel.Connection = connExcel;
connExcel.Open();
//Bind the Sheets to DropDownList
ddlSheets.Items.Clear();
ddlSheets.Items.Add(new ListItem("--Select Sheet--", ""));
ddlSheets.DataSource=connExcel
.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
ddlSheets.DataTextField = "TABLE_NAME";
ddlSheets.DataValueField = "TABLE_NAME";
ddlSheets.DataBind();
connExcel.Close();
txtTable.Text = "";
lblFileName.Text = Path.GetFileName(FilePath);
Panel2.Visible = true;
Panel1.Visible = false;
}

Related

Retrieving values of the first column from Excel sheet (file with .xlsm extension) with OleDB

I am trying to retrieve values of the first column from the Excel sheet called "SP$".
I have the path:
string path = #"C:\Users\atsurkanu\Desktop" + #"\TemplateClientExtraction_IDEAFIMIT_Conero_QUARTER_20170127.xlsm";
string connectionString = string.Format(#"provider=Microsoft.ACE.OLEDB.12.0;data source={0};Extended Properties=Excel 12.0;", path);
string sheetName = "SP$";
and some code like this one:
using (OleDbConnection con = new OleDbConnection(connectionString))
{
try
{
var dataTable = new DataTable();
con.Open();
var tableschema = con.GetSchema("Tables");
var firstsheet = tableschema.Rows[0]["SP$"].ToString();
string name_query = "SELECT A4 FROM [" + firstsheet + "]";
OleDbDataAdapter da = new OleDbDataAdapter(name_query, con);
da.Fill(dataTable);
con.Close();
But it doesn't work. Please, tell me, how I cat retrieve the first column from Excel sheet.
UPDATE:
I am not sure how it works, but it helps:
var dataTable = new DataTable();
con.Open();
var tableschema = con.GetSchema("Tables");
// To get the first sheet name you use the first row and the column named TABLE_NAME
var firstsheet = tableschema.Rows[0]["TABLE_NAME"].ToString();
string name_query = "SELECT F1 FROM [" + "SP$" + "] WHERE F1 <> ''";
OleDbDataAdapter da = new OleDbDataAdapter(name_query, con);
da.Fill(dataTable);
foreach (DataRow dataRow in dataTable.Rows)
{
foreach (var item in dataRow.ItemArray)
{
listWithElementsFromSPfirstColumn.Add((string)item);
}
}
You can't use "A4" and hope that OleDb understands the row/columns conventions of Excel. For OleDb the sheet is just a DataTable and you need to load it all or provide a WHERE condition to filter the rows you want to retrieve.
It is not clear if your sheet has HEADERs or not. You need to add to your connectionstring the key HDR=NO or HDR=YES. In case the headers are missing then OleDb assign automatically the column names with F1, F2, F3 and so on.
So you can query your sheet with something like this or change the F1 to the header of the column A.
using (OleDbConnection con = new OleDbConnection(connectionString))
{
try
{
var dataTable = new DataTable();
con.Open();
var tableschema = con.GetSchema("Tables");
// To get the first sheet name you use the first row and the column named TABLE_NAME
var firstsheet = tableschema.Rows[0]["TABLE_NAME"].ToString();
string name_query = "SELECT F1 FROM [" + firstsheet + "] WHERE F1 <> ''";
OleDbDataAdapter da = new OleDbDataAdapter(name_query, con);
da.Fill(dataTable);
}
catch .....
}
Now it is up to you to read the datatable and use the content of the column F1 (the A column for Excel)

C# SQL bulk insert from excel into SQL Column mapping between Excel and DataTable

I am trying to insert records into sql but I run into a problem when I try to map the columns between Excel and SQl table. If I take the mapping out, the insert works but data ends up in the wrong fields. The error given is that the data source does not contain the column specified even though it does? Any suggestions to why it is not recognizing the column headers in Excel.enter image description here
protected void LoadFile(object sender, EventArgs e)
{
{
//Upload and save the file
string path = Server.MapPath("~/Files/") + Path.GetFileName(FileUpload1.PostedFile.FileName);
FileUpload1.SaveAs(path);
string conString = string.Empty;
string extension = Path.GetExtension(FileUpload1.PostedFile.FileName);
switch (extension)
{
case ".xls": //Excel 97-03
conString = ("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + "; Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"");
break;
case ".xlsx": //Excel 07 or higher
conString = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source= "+ path +";Extended Properties ='Excel 12.0 Xml;HDR=YES;IMEX=1';");
break;
//conString = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + "; Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';");
}
conString = string.Format(conString, path);
using (OleDbConnection excel_con = new OleDbConnection(conString))
{
excel_con.Open();
string sheet1 = excel_con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null).Rows[0]["TABLE_NAME"].ToString();
OleDbDataAdapter objAdapter1 = new OleDbDataAdapter();
DataSet ds = new DataSet();
DataTable dtExcelData = new DataTable();
OleDbConnection OleDbcon = new OleDbConnection(conString);
OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" + sheet1 + "]", OleDbcon);
OleDbcon.Open();
cmd.Connection = OleDbcon;
objAdapter1 = new OleDbDataAdapter(cmd);
objAdapter1.Fill(ds);
dtExcelData = ds.Tables[0];
excel_con.Close();
using (SqlConnection con = new SqlConnection(ConString))
{
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(con))
{
//Set the database table name
sqlBulkCopy.DestinationTableName = "dbo.mytable";
//[OPTIONAL]: Map the Excel columns with that of the database table
sqlBulkCopy.ColumnMappings.Add("Tag","Tag");
sqlBulkCopy.ColumnMappings.Add("ExitDate","ExitDate");
sqlBulkCopy.ColumnMappings.Add("Exit","Exit");
sqlBulkCopy.ColumnMappings.Add("Amount","Amount");
con.Open();
sqlBulkCopy.WriteToServer(dtExcelData);
con.Close();
}
}
}
}`
Any help is appreciated, thank you!
From your image it appears that your column headings in the excel are not flush left (which could indicate spaces). The Exit an Amount columns look like they have spaces at the start of them, which would break the mappings. I have tried your code and it seems to be working.
For the SQL bulk copy mappings the source column must match exactly the column name in the excel, and the destination column must match exactly the column name in the SQL table you are inserting into.

Import Excel .xslx file into sql server : difficulty in updating table

I am importing excel file into sql server datatbase. The code works fine but the way I am doing currently is deleting (clear the table) the table data.
string ssqltable = "tStudent";
string myexceldataquery = "select id,student,rollno,course from [sheet1$]";
try
{
string sexcelconnectionstring = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source =" + excelfilepath + "; Extended Properties=\"Excel 12.0; HDR=Yes; IMEX=2\"";
string ssqlconnectionstring = "Data Source=DELL\\SQLSERVER1;Trusted_Connection=True;DATABASE=Test;CONNECTION RESET=FALSE";
SqlConnection sqlconn = new SqlConnection(ssqlconnectionstring);
SqlCommand sqlcmd = new SqlCommand(#"MERGE tStudent AS target
USING (select ID, STUDENT , ROLLNO from #source) as source
ON (source.ID = target.ID)
WHEN MATCHED THEN
UPDATE SET Student = source.Student,
ROLLNO = source.ROLLNO
WHEN NOT MATCHED THEN
INSERT (ID, STUDENT , ROLLNO)
VALUES (source.id, source.Student, source.RollNo);", sqlconn);
******************************************
SqlParameter param = new SqlParameter();
sqlcmd.Parameters.AddWithValue("#source", dr);
param.SqlDbType = SqlDbType.Structured;
param.TypeName = "dbo.tStudent";
******************************************
sqlconn.Open();
sqlcmd.ExecuteNonQuery();
sqlconn.Close();
//series of commands to bulk copy data from the excel file into our sql table
OleDbConnection oledbconn = new OleDbConnection(sexcelconnectionstring);
OleDbCommand oledbcmd = new OleDbCommand(myexceldataquery, oledbconn);
oledbconn.Open();
OleDbDataReader dr = oledbcmd.ExecuteReader();
SqlBulkCopy bulkcopy = new SqlBulkCopy(ssqlconnectionstring);
bulkcopy.DestinationTableName = ssqltable;
bulkcopy.WriteToServer(dr);
while (dr.Read())
{
//bulkcopy.WriteToServer(dr);
}
oledbconn.Close();
Console.WriteLine(".xlsx file imported succssessfully into database.", bulkcopy.NotifyAfter);
}
See * section. I have assigned my OleDb DataRreader dr in Sqlparameters, but I am declaring it later in code. Please guide me with how to structure my code.
Example would be appreciated.
Given that your excel file is the same structure as your table and you want to update rather than just insert the easiest way is to use Merge and a Table-Valued Paramter
SqlCommand cmd = new SqlCommand(#"MERGE tStudent AS target
USING (select ID, STUDENT , ROLLNO from #source) as source
ON (source.ID = target.ID)
WHEN MATCHED THEN
UPDATE SET Student = source.Student,
ROLLNO = source.ROLLNO
WHEN NOT MATCHED THEN
INSERT (ID, STUDENT , ROLLNO)
VALUES (source.id, source.Student, source.RollNo);"
, sqlconn);
SqlParameter param cmd.Parameters.AddWithValue("#source", dr);
param.SqlDbType = SqlDbType.Structured;
param.TypeName = "dbo.tStudent";
Your other options involve looping, using staging tables, passing the data as xml data or string data, or using an ETL tool like SSIS.

Insert new DataRow with autoincrement Primary Key with C# (OleDB)

I' m trying to insert a new DataRow (locally stored in a DataSet) to a Access table with C#. The table is also created by this app and has 3 columns:
ID (integer, primary key, Required is set to No though I've set it as primary key)
Name (string)
Money (int)
My current code looks like that:
OleDbConnection con = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter da;
DataSet ds = new DataSet();
DataView dv = new DataView();
DataRow row;
string Con = #"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=";
string path = "V:\\ProjectProgress\\Test.mdb";
con.ConnectionString = Con + path;
if (con.State == ConnectionState.Closed)
{
con.Open();
}
cmd.Connection = con;
cmd.CommandText = "SELECT ID, Name, Money FROM Test";
da = new OleDbDataAdapter(cmd);
da.TableMappings.Add("Table", "Test");
da.Fill(ds, "Test");
ds.Tables["Test"].Columns[0].AutoIncrement = true;
ds.Tables["Test"].Columns[0].AutoIncrementSeed = -1;
ds.Tables["Test"].Columns[0].AutoIncrementStep = -1;
dv.Table = ds.Tables["Test"];
row = ds.Tables["Test"].NewRow();
row["Name"] = "Huber";
row["Money"] = 100;
ds.Tables["Test"].Rows.Add(row);
string strOLE = "INSERT INTO Test ([Name], [Money]) Values(#Name, #Money)";
OleDbCommand cmdi = new OleDbCommand(strOLE, con);
cmdi.Parameters.Add("#Name", OleDbType.VarChar, 25, "Name");
cmdi.Parameters.Add("#Money", OleDbType.Integer, 4, "Money");
da.InsertCommand = cmdi;
da.Update(ds.Tables["Test"]);
con.Close();
When updating I'm always getting a
Index or primary key cannot contain a Null value
error.
Setting the Required value of the ID column to Yes, will throw a
Index or Primary Key Cannot Contain a Null Value
error.
How can I let Access assign the right primary key and how do I get the new value back into my dataset?
Using SCOPE_IDENTITY() is not possible in Access as far as I know and found in some forums.
(Working with Visual C# Express 2010, Access 2003)
The following is complete working test code to illustrate the procedure. All we need to do is provide an OleDbDataAdapter.SelectCommand that includes the primary key and the columns we want to update, and the OleDbCommandBuilder object will create the INSERT statement for us:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
namespace oleDbTest
{
class Program
{
static void Main(string[] args)
{
string myConnectionString;
myConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=C:\Users\Public\Database1.accdb;";
using (OleDbConnection con = new OleDbConnection())
{
con.ConnectionString = myConnectionString;
con.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter())
using (OleDbCommandBuilder bld = new OleDbCommandBuilder(da))
{
bld.QuotePrefix = "["; // these are
bld.QuoteSuffix = "]"; // important!
da.SelectCommand = new OleDbCommand(
"SELECT [ID], [Name], [Money] " +
"FROM [Test] " +
"WHERE False",
con);
using (System.Data.DataTable dt = new System.Data.DataTable("Test"))
{
// create an empty DataTable with the correct structure
da.Fill(dt);
System.Data.DataRow dr = dt.NewRow();
dr["Name"] = "Huber";
dr["Money"] = 100;
dt.Rows.Add(dr);
da.Update(dt); // write new row back to database
}
}
con.Close();
}
Console.WriteLine();
Console.WriteLine("Done.");
}
}
}
My first thought is that your problem might be that both AutoIncrementSeed and AutoIncrementStep are set to a negative value of -1. Try setting both to a positive value.
[EDIT]
Second thought, you might want to try the OleDbCommandBuilder (MSDN documentation here). It creates your INSERT, UPDATE, and DELETE statements automatically using the column information of your DataSet.
At my former employer we used OleDbCommandBuilder all the time when working with Access and it worked like a charm, even with auto increment fields.

Reading Excel in .NET, how to get specific rows?

I have got the following code from here to read an Excel file using C# .NET:
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Book1.xls;Extended Properties=""Excel 8.0;HDR=YES;""";
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OleDb");
DbCommand command = connection.CreateCommand()
command.CommandText = "SELECT City,State FROM [Cities$]";
I want to select only 10 rows starting from the 4th row, how can I do that?
I'm no Excel querying expert, but this certainly works. Use TOP to limit the query to 13 rows. The first row is the header with column names so it may not count. Obviously change if I misunderstand. Then, keep track of a row ID and do stuff to rows on or after 4.
Hope this helps!
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Book1.xls;Extended Properties=""Excel 8.0;HDR=YES;""";
using (System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connectionString)) {
System.Data.OleDb.OleDbCommand cmd = new System.Data.OleDb.OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT top 13 City,State FROM [Cities$]";
conn.Open();
System.Data.IDataReader dr = cmd.ExecuteReader();
int row = 2;
while (dr.Read()) {
if (row++ >= 4) {
// do stuff
Console.WriteLine("{0}, {1}", dr[0], dr[1]);
}
}
}

Categories