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

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.

Related

How does sanitize the sql parameter

I write these code all are working fine but there is a warning coming that sanitize the sql parameter.
private DataSet ExcelToDataSet(string fileData)
{
DataSet ds = new DataSet();
string connectionString = GetConnectionString(fileData);
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
// Get all Sheets in Excel File
DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
// Loop through all Sheets to get data
foreach (DataRow dr in dtSheet.Rows)
{
string sheetName = dr["TABLE_NAME"].ToString();
if (!sheetName.EndsWith("$"))
continue;
// Get all rows from the Sheet
cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
DataTable dt = new DataTable();
dt.TableName = sheetName;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
ds.Tables.Add(dt);
}
cmd = null;
conn.Close();
}
return (ds);
}
I have to sanitize the following line
cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
Usually, when writing SQL Statements, you need to use parameters to pass the data from user input into the sql statement, to prevent SQL Injection attacks. That's why you get this warning. However, there is nothing you can do about it since it's impossible to parameterize identifiers in SQL, and you don't need to do it because you are not concatenating user input, and you are not running this query on a database, so even if you could use SQL injection, the worst you can do is corrupt a single file
UPDATE: I did not notice this was a OleDbConnection, the database you are connecting to may not have the same functionality to quote an identifier. I am leaving this answer here in case someone comes across this question and needs the same thing but for a SQL connection.
As the others have said, there is no need to worry about the warning in this case as the data is not coming from user data.
However everyone is wrong about the fact you cannot parameterize an identifier. You need to build the query dynamically server side and use the QUOTENAME function but it is possible.
foreach (DataRow dr in dtSheet.Rows)
{
string sheetName = dr["TABLE_NAME"].ToString();
if (!sheetName.EndsWith("$"))
continue;
// Get all rows from the Sheet
cmd.CommandText = #"
declare #sql nvarchar(114);
set #sql = N'select * from ' + quotename(#sheetname)
exec sp_executesql #sql
";
cmd.Parameters.Clear();
cmd.Parameters.Add("#sheetname", SqlDbType.NVarChar, 100).Value = sheetName;
DataTable dt = new DataTable();
dt.TableName = sheetName;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
ds.Tables.Add(dt);
}
This will generate a dynamic query that will safely escape the name of the table.

How to insert date while importing Excel into SQL Server

I loaded an Excel file into SQL Server and it's working fine, but I want to attach current date while importing this file. That means each row will have the date data loaded in. So each time I load the file, new data should take the current date, and the old data still have the old date.
How can I do that ?
Code for importing the Excel file:
string ssqltable = comboBox1.GetItemText(comboBox1.SelectedItem);
string myexceldataquery = "select * from [" + ssqltable + "$]";
try
{
OleDbConnection oconn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + imagepath + ";Extended Properties='Excel 12.0 Xml; HDR=YES;IMEX=1;';");
string ssqlconnectionstring = "Data Source=.;Initial Catalog=Bioxcell;Integrated Security=true";
OleDbCommand oledbcmd = new OleDbCommand(myexceldataquery, oconn);
oconn.Open();
SqlBulkCopy bulkcopy = new SqlBulkCopy(ssqlconnectionstring);
DataTable dt = new DataTable();
dt.Load(oledbcmd.ExecuteReader());
bulkcopy.DestinationTableName = ssqltable;
for (int i = 0; i < dt.Columns.Count; i++)
{
bulkcopy.ColumnMappings.Add(i, i);
}
bulkcopy.WriteToServer(dt);
oconn.Close();
}
and this for inserting date but not working for me
if (ssqltable == "Overseas")
{
conn.Open();
SqlCommand sqlc = new SqlCommand("delete from Overseas where Bonus = 'Bonus'", conn);
sqlc.ExecuteScalar();
SqlCommand Update1 = new SqlCommand("Update Overseas set ID = 1 Where ProductCode = 9630", conn);
Update1.ExecuteNonQuery();
SqlCommand Update2 = new SqlCommand("Update Overseas set ID = 2 Where ProductCode = 9628", conn);
Update2.ExecuteNonQuery();
SqlCommand Update3 = new SqlCommand("Update Overseas set ID = 3 Where ProductCode = 9629", conn);
Update3.ExecuteNonQuery();
SqlCommand Update4 = new SqlCommand("Update Overseas set ID = 4 Where ProductCode = 9632", conn);
Update4.ExecuteNonQuery();
SqlCommand Update5 = new SqlCommand("Update Overseas set ID = 5 Where ProductCode = 9631", conn);
Update5.ExecuteNonQuery();
SqlCommand Update6 = new SqlCommand("insert into Overseas (Date) Values (GETDATE())", conn);
Update6.ExecuteNonQuery();
}
No error and only one date was inserted. How can I solve this problem ?
If you have control over the table, you can create a new date column with a default value of GETDATE():
ALTER TABLE <tablename>
ADD ROW_CREATE_TS DATETIME DEFAULT GETDATE()
Ref: https://learn.microsoft.com/en-us/sql/relational-databases/tables/specify-default-values-for-columns

How to insert a datatable to an access database in C#

I have 2 table in an access database
now I want to select from one table and insert them into another one.
this is my code but it shows an exception in line Cmd.ExecuteNonQuery();
{"Syntax error (missing operator) in query expression 'System.Object[]'."}
the code is :
public static void SetSelectedFeedIntoDB(Form2 frm2)
{
string StrCon = System.Configuration.ConfigurationManager.ConnectionStrings["FeedLibraryConnectionString"].ConnectionString;
OleDbConnection Connection = new OleDbConnection(StrCon);
OleDbDataAdapter DataA = new OleDbDataAdapter("Select * from FeedLibrary where ID=" + frm2.FeedSelectListBox.SelectedValue, Connection);
DataTable DTable = new DataTable();
DataA.Fill(DTable);
OleDbCommand Cmd = new OleDbCommand();
Cmd.Connection = Connection;
Connection.Open();
foreach (DataRow DR in DTable.Rows)
{
Cmd.CommandText = "insert into SelectedFeeds Values(" + DR.ItemArray + ")";
Cmd.ExecuteNonQuery();
}
Connection.Close();
}
what should I do to fix this?
Your error is caused by the fact that you are concatenating the ItemArray property of a DataRow to a string. In this case the ItemArray (that is an instance of an object[]) has no method that automatically produces a string from its values and thus returns the class name as a string "object[]" but of course this produces the meaningless sql string
"insert into SelectedFeeds Values(object[])";
But you could simply build a SELECT .... INTO statement that will do everything for you without using DataTables and Adapters
string cmdText = #"SELECT FeedLibrary.* INTO [SelectedFeeds]
FROM FeedLibrary
where ID=#id";
using(OleDbConnection Connection = new OleDbConnection(StrCon))
using(OleDbCommand cmd = new OleDbCommand(cmdText, Connection))
{
Connection.Open();
cmd.Parameters.Add("#id", OleDbType.Integer).Value = Convert.ToInt32( frm2.FeedSelectListBox.SelectedValue);
cmd.ExecuteNonQuery();
}
However, the SELECT ... INTO statement creates the target table but gives error if the target table already exists. To solve this problem we need to discover if the target exists. If it doesn't exist we use the first SELECT ... INTO query, otherwise we use a INSERT INTO ..... SELECT
// First query, this creates the target SelectedFeeds but fail if it exists
string createText = #"SELECT FeedLibrary.* INTO [SelectedFeeds]
FROM FeedLibrary
where ID=#id";
// Second query, it appends to SelectedFeeds but it should exists
string appendText = #"INSERT INTO SelectedFeeds
SELECT * FROM FeedLibrary
WHERE FeedLibrary.ID=#id";
using(OleDbConnection Connection = new OleDbConnection(StrCon))
using(OleDbCommand cmd = new OleDbCommand("", Connection))
{
Connection.Open();
// Get info about the SelectedFeeds table....
var schema = Connection.GetSchema("Tables",
new string[] { null, null, "SelectedFeeds", null});
// Choose which command to execute....
cmd.CommandText = schema.Rows.Count > 0 ? appendText : createText;
// Parameter #id is the same for both queries
cmd.Parameters.Add("#id", OleDbType.Integer).Value = Convert.ToInt32( frm2.FeedSelectListBox.SelectedValue);
cmd.ExecuteNonQuery();
}
Here we have two different queries, the first one create the SelectedFeeds table as before, the second one appends into that table.
To discover if the target table has already been created I call Connection.GetSchema to retrieve a datatable (schema) where there is a row if the table SelectedFeeds exists or no row if there is no such table.
At this point I set the OleDbCommand with the correct statement to execute.

How to Insert Rows to Table Object Inside an Excel Sheet?

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;
}

Use OleDbDataAdapter to insert into an Access db

I would like to insert some data into an Access Database.
DataTable dt = new DataTable();
String sql = string.Format("SELECT * FROM {0} where 1=0; ", tmap.SqlTableName);
string con = string.Format(conn, accessPath);
OleDbDataAdapter da = new OleDbDataAdapter(sql, con);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(da);
da.InsertCommand = cmdBuilder.GetInsertCommand(true); // Returns "INSERT INTO test (int, bdate, amt, text, bit) VALUES (?, ?, ?, ?, ?)"
da.Fill(dt);
//Add data to the DateTable
for (int i = 0; i < rowCount; i++)
{
DataRow dr = dt.NewRow();
//....
dt.Rows.Add(dr);
}
da.Update(dt); //This is where things go south.
System.Data.OleDb.OleDbException
Message: Syntax error in INSERT INTO statement.
Source: Microsoft JET Database Engine.
If I change the insert command:
da.InsertCommand = new OleDbCommand("INSERT INTO test ([text]) VALUES (?)");
and change the incoming data to only have a single text value I get:
No value given for one or more required parameters.
Am I missing something?
The issue was in the data types. The code in the question works if the data types are compatible.
Make sure all required columns are included in the insert query.
If It doesn't work then create a new method for inserting and follow this:
OleDbConnection conn = new OleDbConnection (connectionString);
OleDbCommand command = new OleDbCommand();
command.Connection = conn;
command.CommandText= "INSERT INTO myTable (col1, col2) VALUES (#p_col1, #p_col2)";
command.Parameters.Add ("#p_col1", OleDbType.String).Value = textBox1.Text;
...
command.ExecuteNonQUery();

Categories