I've been working with a program where I import 2 excel files and those excel files have different columns names.. so it could be the possibility for a user to import the wrong excel file (with other column names) and my problem is that I'm reading the data from excel with OledbDataAdapter so I have to specified the name of each column, so when the user import the wrong file the program stop working (because the program can't find the right columns to get the data).
Okay so my question is, is there a way to check if a column exist in specific excel sheet?
So I'll be able to do something if the column doesn't exist in the file the user imported...
Here's a part of my code:
OleDbCommand command1 = new OleDbCommand(
#"SELECT DISTINCT serie FROM [Sheet1$]
WHERE serie =#MercEnInventario AND serie IS NOT NULL", connection);
command1.Parameters.Add(new OleDbParameter("MercEnInventario", MercInv));
string serieN = Convert.ToString(command1.ExecuteScalar());
readerOle = command1.ExecuteReader();
readerOle.Close();
I got an OleDbException when I try to give value to the string 'serieN' because the column name 'serie' doesn't exists in the excel file the user imported.
If you can help me I'll be so grateful :)
OleDbConnection has GetOleDbSchemaTable command that allows you to retrieve just the list of columns. An example code would be
DataTable myColumns = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns, new object[] { null, null, "Sheet1$", null });
This will return a DataTable, populated with column information (names, types and more). You can then loop thru Rows collection examining "COLUMN_NAME" column, something like
foreach (DataRow dtRow in myColumns.Rows)
{
if (dtRow["COLUMN_NAME"].ToString() == "serieN") // Do your stuff here ....
}
How about this:
public bool FieldExists(OleDbConnection connection, string tabName, string fieldName)
{
var adapter = new OleDbDataAdapter(string.Format("SELECT * FROM [{0}]", tabName), connection);
var ds = new DataSet();
adapter.Fill(ds, tabName);
foreach (var item in ds.Tables[tabName].Rows[0].ItemArray)
{
if (item.ToString() == fieldName)
return true;
}
return false;
}
Related
I know it's been asked many times and there's so many resources about this but believe me i tried those, Unfortunately same thing is always happen. I really don't know why my combo box column value is repeating. Can someone help me in doing these in a proper way. Did i forgot something here ? Thank you
public void FillComboBox()
{
using (var con = SQLConnection.GetConnection())
{
using (var cmd = new SqlCommand("SELECT * FROM employee_product", con))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
cbox_order.Items.Add("Code").ToString();
cbox_order.Items.Add("Model").ToString();
cbox_order.Items.Add("Itemdescription").ToString();
}
}
}
}
}
Here's the provided image
If you check the code, you are basically just adding the strings "Code", "Model" and "Itemdescription" to the combobox. I guess you want rather something like:
while (reader.Read())
{
cbox_order.Items.Add($"{reader["Code"]} {reader["Model"]} {reader["Itemdescription"]}");
}
In this snippet I am using the reader to get values of the columns in the returned row from the DB and then displaying joining those values in a single string that is then added to the ComboBox as an item.
Update
If you know the column names, why not just do this?
public void FillComboBox()
{
cbox_order.Items.Add("Code").ToString();
cbox_order.Items.Add("Model").ToString();
cbox_order.Items.Add("Itemdescription").ToString();
}
First load data into a DataTable:
var connection = #"Your connection string";
var command = "Your SELECT command text";
var table = new DataTable();
using (var adapter = new SqlDataAdapter(command, connection))
adapter.Fill(table);
To show list of columns in a ComboBox:
comboBox1.DataSource = table.Columns.Cast<DataColumn>().ToList();
comboBox1.ValueMember = "ColumnName";
comboBox1.DisplayMember = "ColumnName";
To show data in DataGridView:
dataGridView1.DataSource = table;
In above code I suppose you are going to show columns of the table which you also want to load its data at the same time. In case which you just want to load just column information, you can use:
adapter.FillSchema(table, SchemaType.Mapped);
actually your not using your DataReader but you just add Code, Model and ItemDescription item for each row found with the MySQL query.
cbox_order.Items.Add("Code").ToString();
cbox_order.Items.Add("Model").ToString();
cbox_order.Items.Add("Itemdescription").ToString();
If you want to use the result of the MySQL query you can try this instead:
cbox_order.Items.Add(reader["Code"].ToString()).ToString(); // Change "Code" by the column name into the database
cbox_order.Items.Add(reader["Model"].ToString()).ToString(); // Change "Model" by the column name into the database
cbox_order.Items.Add(reader["Itemdescription"].ToString()).ToString(); // Change "Itemdescription" by the column name into the database
Don't forget to close the reader at the end
reader.Close();
EDIT
if you want the column name instead of data you can use this query, but that's useless if you already know the column name.
SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema='databasename' AND table_name='tablename'
Try to close and dispose of reader and close the connection.
reader.close;
reader.dispose;
con.close();
I try to import an excel in to my DataTable with condition.
example : - The user have my provided excel import template, with the first row 0 as the column header (ItemCode, QTY, SerialNo, Remarks). But due to the user might accidentally insert few unwanted column name in anywhere of my per-ready column or delete one of my column name.
I try to build a code regardless what happen, the system only detect my standard ready column header (ItemCode, QTY, SerialNo, Remarks). And only will add the column still within the excel and ignore those accidentally delete column name.
What is the best way to code the detection of the column name when is exist before allow to import those specific column into dataTable?
Below is my current excel import code (which i try to add the above condition code)
private DataTable ReadExcelToDataTable(string filePath)
{
tableSalesOrder = new DataTable("dtSO");
string strConn = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1;TypeGuessRows=0;ImportMixedTypes=Text\"", filePath);
using (OleDbConnection dbConnection = new OleDbConnection(strConn))
{
dbConnection.Open();
DataTable dtExcelSchema = dbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string sSheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
dbConnection.Close();
using (OleDbDataAdapter dbAdapter = new OleDbDataAdapter("SELECT DISTINCT * FROM [" + sSheetName + "]", dbConnection)) //rename sheet if required!
dbAdapter.Fill(tableSalesOrder);
}
return tableSalesOrder;
}
I have try to google around, found many hint but still unable to make it work.
Thank you for any advice.
If you just wanted to ignore extra columns, you could use
... new OleDbDataAdapter("Select Distinct ItemCode, QTY, SerialNo, Remarks FROM [" + sSheetName + "] ...
If you need to cope with some of these columns being missing, then it is slightly more complicated. You need to get a list of columns in the excel sheet , eg
DataTable dt = dbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns,
new object[] { null,null, sSheetName, null });
List<string> columnNames = new List<string>();
foreach (DataRow row in dt.Rows)
columnNames.Add(row["Column_name"].ToString());
Then you need to build up your select statement to only include the columns that you want and that exist in the excel sheet.
You could set up your workbooks with named ranges, and extract those. That way it'll work even if they accidentally change the name or insert extra columns. You can select the named range with something like this:
var sql = "SELECT * FROM [Workbook$MyNamedRange]"
using (OleDbDataAdapter dbAdapter = new OleDbDataAdapter(sql, dbConnection));
dbAdapter.Fill(tableSalesOrder);
I solve the issue by using different method, I got the idea from both your guys advise. In fact, after i do some test on my origin code which posted, it only will import according the column name which i define which is ItemCode, Qty, SerialNo & Remakrs at my gridView which my dataTable have assign to it as data source.
The only issue is when one of the column deleted, my import process will be facing problem. This due to the datatable never assign any column name after it created.
I solve it by improve the dataTable set and redefine the column name into It.
if (tableSalesOrder == null)
{
tableSalesOrder = new DataTable("dtSO");
DataColumn colItemCode = new DataColumn("ItemCode",typeof(string));
......
tableSalesOrder.Columns.Add(colItemCode);
......
}
else
{
tableSalesOrder.Clear();
}
Thanks guys for the help. Finally I found where the bugs were.
I'm currently using Mono on Ubuntu with MonoDevelop, running with a DataTable matching a table in the database, and should be attempting to update it.
The code following uses a Dataset loaded from an XML file, which was created from a Dataset.WriteXML on another machine.
try
{
if(ds.Tables.Contains(s))
{
ds.Tables[s].AcceptChanges();
foreach(DataRow dr in ds.Tables[s].Rows)
dr.SetModified(); // Setting to modified so that it updates, rather than inserts, into the database
hc.Data.Database.Update(hc.Data.DataDictionary.GetTableInfo(s), ds.Tables[s]);
}
}
catch (Exception ex)
{
Log.WriteError(ex);
}
This is the code for inserting/updating into the database.
public override int SQLUpdate(DataTable dt, string tableName)
{
MySqlDataAdapter da = new MySqlDataAdapter();
try
{
int rowsChanged = 0;
int tStart = Environment.TickCount;
da.SelectCommand = new MySqlCommand("SELECT * FROM " + tableName);
da.SelectCommand.Connection = connection;
MySqlCommandBuilder cb = new MySqlCommandBuilder(da);
da.UpdateCommand = cb.GetUpdateCommand();
da.DeleteCommand = cb.GetDeleteCommand();
da.InsertCommand = cb.GetInsertCommand();
da.ContinueUpdateOnError = true;
da.AcceptChangesDuringUpdate = true;
rowsChanged = da.Update(dt);
Log.WriteVerbose("Tbl={0},Rows={1},tics={2},", dt.TableName, rowsChanged, Misc.Elapsed(tStart));
return rowsChanged;
catch (Exception ex)
{
Log.WriteError("{0}", ex.Message);
return -1
}
I'm trying the above code, and rowsChanged becomes 4183, the number of rows I'm editing. However, when I use HeidiSQL to check the database itself, it doesn't change anything at all.
Is there a step I'm missing?
Edit: Alternatively, being able to overwrite all rows in the database would work as well. This is a setup for updating remote computers using USB sticks, forcing it to match a source data table.
Edit 2: Added more code sample to show the source of the DT. The DataTable is prefilled in the calling function, and all rows have DataRow.SetModified(); applied.
Edit 3: Additional information. The Table is being filled with data from an XML file. Attempting fix suggested in comments.
Edit 4: Adding calling code, just in case.
Thank you for your help.
The simplest way which you may want to look into might be to TRUNCATE the destination table, then simply save the XML import to it (with AI off so it uses the imported ID if necessary). The only problem may be with the rights to do that. Otherwise...
What you are trying to do can almost be handled using the Merge method. However, it can't/won't know about deleted rows. Since the method is acting on DataTables, if a row was deleted in the master database, it will simply not exist in the XML extract (versus a RowState of Deleted). These can be weeded out with a loop.
Likewise, any new rows may get a different PK for an AI int. To prevent that, just use a simple non-AI PK in the destination db so it can accept any number.
The XML loading:
private DataTable LoadXMLToDT(string filename)
{
DataTable dt = new DataTable();
dt.ReadXml(filename);
return dt;
}
The merge code:
DataTable dtMaster = LoadXMLToDT(#"C:\Temp\dtsample.xml");
// just a debug monitor
var changes = dtMaster.GetChanges();
string SQL = "SELECT * FROM Destination";
using (MySqlConnection dbCon = new MySqlConnection(MySQLOtherDB))
{
dtSample = new DataTable();
daSample = new MySqlDataAdapter(SQL, dbCon);
MySqlCommandBuilder cb = new MySqlCommandBuilder(daSample);
daSample.UpdateCommand = cb.GetUpdateCommand();
daSample.DeleteCommand = cb.GetDeleteCommand();
daSample.InsertCommand = cb.GetInsertCommand();
daSample.FillSchema(dtSample, SchemaType.Source);
dbCon.Open();
// the destination table
daSample.Fill(dtSample);
// handle deleted rows
var drExisting = dtMaster.AsEnumerable()
.Select(x => x.Field<int>("Id"));
var drMasterDeleted = dtSample.AsEnumerable()
.Where( q => !drExisting.Contains(q.Field<int>("Id")));
// delete based on missing ID
foreach (DataRow dr in drMasterDeleted)
dr.Delete();
// merge the XML into the tbl read
dtSample.Merge(dtMaster,false, MissingSchemaAction.Add);
int rowsChanged = daSample.Update(dtSample);
}
For whatever reason, rowsChanged always reports as many changes as there are total rows. But changes from the Master/XML DataTable do flow thru to the other/destination table.
The delete code gets a list of existing IDs, then determines which rows needs to be deleted from the destination DataTable by whether the new XML table has a row with that ID or not. All the missing rows are deleted, then the tables are merged.
The key is dtSample.Merge(dtMaster,false, MissingSchemaAction.Add); which merges the data from dtMaster with dtSample. The false param is what allows the incoming XML changes to overwrite values in the other table (and eventually be saved to the db).
I have no idea whether some of the issues like non matching AI PKs is a big deal or not, but this seems to handle all that I could find. In reality, what you are trying to do is Database Synchronization. Although with one table, and just a few rows, the above should work.
I am attempting to loop through a dataset's rows and columns in search for a match between the dataset's name column -- and the ColumnName from a DataReader object.
I have a new table called RECORDS which is empty at program startup. I also have a pre-populated table called ColumnPositions with a sub-set of column names found in the RECORDS table. This routine is intended to show a subset of all the available columns -- as a default display style.
My code works...except for the line of code that gets the dr["type"] value. I get the error:
The name 'colType' does not exist in the current context.
As you can clearly see, my string variables are declared outside the WHILE and FOREACH loops. The line statement colName = works just fine. But colType fails everytime. If I do a statement check in the Intermediate Window in VS2010 for ? dr["type"]" I get the result integer. But when I check ? colType, I get the above noted error message.
The intellisense for the DataRow object dr reveals an array of 6 items. Index 1 in the array maps to name. Index 2 maps to type. When I check the value of ? dr[2] in the Intermediate Window, the same result comes back integer. This is correct. But whenever this value is assigned to colType, VS2010 complains.
I'm no newbie to C# so I did a lot of testing and Googling before posting here. I'm hoping that this is a matter of me not seeing the forest through the trees.
Here's my code:
// get table information for RECORDS
SQLiteCommand tableInfo = new SQLiteCommand("PRAGMA table_info(Records)", m_cnCaseFile);
SQLiteDataAdapter adapter = new SQLiteDataAdapter(tableInfo);
DataSet ds = new DataSet();
adapter.Fill(ds);
DataTable dt = ds.Tables[0];
SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM ColumnPositions WHERE ColumnStyle_ID = " + styleID + " ORDER BY ColumnPosition_ID ASC", m_cnCaseFile);
SQLiteDataReader colReader = cmd.ExecuteReader();
string colName = "";
string colType = "";
if (dt != null && colReader.HasRows)
{
while (colReader.Read())
{
foreach(DataRow dr in dt.Rows)
{
colType = Convert.ToString(dr["type"]);
colName = dr["name"].ToString();
if (colReader["ColumnName"].ToString() == colName)
{
DataGridViewColumn dgvCol = new DataGridViewColumn();
}
}
}
}
dt.Dispose();
colReader.Close();
Instead of using "dr["name"].ToString();", it is better to use "Convert.ToString(dr["name"]);"
Try using the array position instead of the column name:
colType = Convert.ToString(dr[1]);
and
colName = dr[0].ToString();
You probably don't need this, but here is the documentation for values returned by the SQLite PRAGMA table_info() command. LINK
I am using C#.NET 3.5 in VS2010 on Win 7 machine. I have an Excel sheet and I want to extract the data stored in it. I know how to parse the Excel when given the range of cells. Here we connect using OLEDB, give the SQL command and map this to a DataTable, then access the data.
select * from [sheetname$A2:N50]
where "sheetname" is the name of Excel sheet and "$A2:N50" is the range of cells.
BUT, BUT, my requirement is totally different.
I can't hardcode the cell range like above, because the location of data cells may change dynamically. For example: data stored in cell A20 may be changed to C14 in the very next execution.
I need to parse my Excel sheet based on some key word searching. I mean I should search for the keyword "XYZ" and than parse the table just below it. This key word may change its position for every execution.
Since I don't know the cell range, I can't even get the Excel data into a DataTable using the above query.
Instead of selecting from a cell range you can fill all the data into a datatable and query from that.
DataTable dt = new DataTable();
try
{
OleDbConnection con = new OleDbConnection(string.Format(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=""Excel 8.0;HDR=yes;IMEX=1""", excelPath));
OleDbDataAdapter da = new OleDbDataAdapter("select * from [sheetname$]", con);
da.Fill(dt);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
//now you can use dt DataTable
foreach (DataRow dr in dt.Rows)
{
//....
}
hope this helps...