I want to include an option in my program to export some data from the database to an excel file. There are multiple columns in my database table, but only two will be exported (item and quantity). The 'category' indicates which dgv the data is part of.
But anyways, back to the issue...how do I read data from my database to a new excel file?
Here is my code so far:
private void excelToolStripMenuItem_Click(object sender, EventArgs e)
{
//EXPORT TO EXCEL
string fname = "Inventory Report.xls";
Workbook wb = new Workbook();
Worksheet ws1 = new Worksheet("Electrical");
Connection();
sqlconnection.Open();
//string dbQuery = "SELECT * FROM inventory_table WHERE category= 0";
sqlcmd = new SqlCommand("SELECT * FROM inventory_table WHERE category= 0", sqlconnection);
using (SqlDataReader sqldr = sqlcmd.ExecuteReader())
{
if (sqldr.HasRows)
{
while (sqldr.Read())
{
//HOW DO I READ TO EXCEL???
}
sqldr.Close();
wb.Worksheets.Add(ws1);
wb.Save(fname);
}
}
}
UPDATED:
private void excelToolStripMenuItem_Click(object sender, EventArgs e)
{
Connection();
sqlconnection.Open();
using (sqlcmd = new SqlCommand("SELECT * FROM inventory_table WHERE category= 0"))
{
using (SqlDataAdapter sqlda = new SqlDataAdapter())
{
sqlcmd.Connection = sqlconnection;
sqlda.SelectCommand = sqlcmd;
using (DataTable dt = new DataTable())
{
sqlda.Fill(dt);
using (XLWorkbook wb = new XLWorkbook())
{
string fname = "Inventory Report.xlsx";
wb.Worksheets.Add(dt, "inventory_table");
wb.SaveAs(fname);
}
}
}
}
}
If you are creating an excel doc via a web app or something similar you might check out EPPlus which is available as a NuGet package and seems to do what you need.
Otherwise take a look at the MS docs regarding using office interop objects. Start by adding a reference for Microsoft.Office.Interop.Excel, then adding a using statement to your code:
using Excel = Microsoft.Office.Interop.Excel;
Then create a new excel document like this:
var excelDoc = new Excel.Application();
excelDoc.Visible = true;
excelDoc.Workbooks.Add();
Excel._Worksheet wksht = (Excel.Worksheet)excelDoc.ActiveSheet;
Finally, you'll need to loop through the data you fetched from the DB and insert it somewhere:
wksht.Cells[1, "A"] = "Some data";
Granted, you'll likely want to use index variables that you can increment as you loop through. Hope this helps!
Edit
As far as getting the appropriate data is concerned, just make a quick edit to your SQL statement. Right now you're using:
SELECT * FROM inventory_table WHERE category = 0
Using * in your select statement will pull in every single column in the table. Instead you should tell it exactly what you want:
SELECT things, stuff, junk FROM inventory_table WHERE category = 0
Alternatively you could use Linq which I find makes cleaner code for queries:
var getThings = (from i in db.inventory_table select i.things);
With that you'll need to add a using statement for System.Linq as well as declare db as an ApplicationDbContext.
Related
As title says I've used odbcconnection to sqlconnection and for the life of me cant get it to work.. Copied a bunch of code from this site but cant get them both to work.
The program just hangs so maybe I am doing something wrong, but would appreciate maybe a bare bones template that i could just fill in the connection details and bulk copy the table to table..
using (OdbcConnection myConnection = new OdbcConnection())
{
string myConnectionString = #"Driver={Microsoft Access Driver (*.mdb)};" +
"Dbq=//####/data/Toronto/wrkgrp/wrkgrp30/Business Risk Oversight and Control/DATA INTEGRITY/CDE/CDE Testing Portal Requirements/Migration Requirements/RCM/Mutual Funds/Mutual Funds.mdb;";
myConnection.ConnectionString = myConnectionString;
myConnection.Open();
//execute queries, etc
OdbcCommand cmd = myConnection.CreateCommand();
cmd.CommandText = "SELECT * FROM RCM_MF_New_Accounts_Samples";
OdbcDataReader reader = cmd.ExecuteReader(); // close conn after complete
DataTable myDataTable = new DataTable();
myDataTable.Load(reader);
//myConnection.Close();
string destinationConnectionString = "Data Source=####;Initial Catalog=DYOF_STAGING_BROC;User ID=;Password=;Connection Timeout=999";
SqlConnection destination = new SqlConnection(destinationConnectionString);
SqlBulkCopy bulkData;
//destination.Open();
Exception ex = null;
try
{
Console.WriteLine("step1");
bulkData = new SqlBulkCopy(destinationConnectionString, SqlBulkCopyOptions.FireTriggers);
bulkData.BulkCopyTimeout = 1;
bulkData.DestinationTableName = "Load_CTR_Sample_Account_Opening2";
bulkData.WriteToServer(myDataTable);
bulkData.Close();
Console.WriteLine("moved from here to there");
reader.Close();
//destination.Close();
}
catch (Exception e)
{
ex = e;
}
I actually wrote an ORM to make this kind of task easier.
var accessDS = new AccessDataSource(connectionString1);
var dt = accessDS.From("RCM_MF_New_Accounts_Samples").ToDataTable().Execute();
var sqlDS = new SqlServerDataSource(connectionString2);
sqlDS.InsertBulk("Load_CTR_Sample_Account_Opening2", dt).Execute();
This does not work for .NET Core.
Packages:
https://www.nuget.org/packages/Tortuga.Chain.Access/
https://www.nuget.org/packages/Tortuga.Chain.SqlServer
Read the data from Access into a DataTable:
string strConnect = #"Provider=Microsoft.ACE.OLEDB.12.0;data source=D:\Temp\MyDB.accdb";
DataTable dt = new DataTable();
using (OleDbConnection con = new OleDbConnection(strConnect))
{
OleDbCommand cmd = new OleDbCommand("SELECT * FROM MyTable", con);
con.Open();
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
}
Then use SqlBulkCopy to update SQL:
string strConnect = #"Data Source=GRIFFPC\SQLEXPRESS;Initial Catalog=Testing;Integrated Security=True";
using (SqlConnection con = new SqlConnection(strConnect))
{
con.Open();
using (SqlBulkCopy bulk = new SqlBulkCopy(con))
{
bulk.DestinationTableName = "Test";
bulk.WriteToServer(dt);
}
}
Of course, there is a much easier way to go straight from Access to SQL Server, using VBA, SQL , or other methods.
https://support.office.com/en-us/article/import-or-link-to-data-in-an-sql-server-database-a5a3b4eb-57b9-45a0-b732-77bc6089b84e
https://www.sqlshack.com/six-different-methods-to-copy-tables-between-databases-in-sql-server/
Here's a basic example of bulk insert.
public void BulkInsert(DataTable employees)
{
if (employees == null)
throw new ArgumentNullException(nameof(employees), $"{nameof(employees)} is null.");
using (var con = OpenConnection())
using (var sbc = new SqlBulkCopy(con))
{
sbc.DestinationTableName = "HR.Employee";
foreach (DataColumn column in employees.Columns)
sbc.ColumnMappings.Add(column!.ColumnName, column.ColumnName);
sbc.WriteToServer(employees);
}
}
Note the foreach (DataColumn column in employees.Columns) loop. This is required so that it knows the column names are the same in the source and the target table. (If they're not the same, manually map them in the same fashion.)
Source: https://grauenwolf.github.io/DotNet-ORM-Cookbook/BulkInsert.htm#ado.net
Following option need to verify
1) Column Name should be the same.
2) verify the column length.
3) verify the data type.
I'm just learning and have written this code to copy data from one database to another (LOTSCON) = source, CON = destination.
The code all works and copies the data across, however it is based on checkboxes on a previous datagridview form.
The user selects which records to import, but also selects CHKCOMMUNITY which means this patient is a nursing home patient.
In the NEW table, there is a column nursinghome which is a bit type.
If the user ticks chkCommunity in the datagrid, I want to do the bulk copy but also make sure the nursinghome column in the destination table is set to 1.
So I'm not mapping an existing column in the source table..
How can I achieve this?
DO I just import then run a SQL string updating the column based on what I have just entered?
foreach (DataGridViewRow row in dataGridInst.Rows)
{
DataGridViewCheckBoxCell chkcell = row.Cells["chkimport"] as DataGridViewCheckBoxCell;
if (chkcell.Value != null)
{
if (Convert.ToBoolean(chkcell.Value) == true)
{
instid = Convert.ToInt32(row.Cells["clninstid"].Value);
iscommunity = Convert.ToInt32(row.Cells["chkcommunity"].Value);
using (SqlConnection lotscon = new SqlConnection(ConfigurationManager.ConnectionStrings["LOTSConnectionString"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(#"SELECT Person.*, NEWinstitution.institutionname
FROM NEWinstitution INNER JOIN Person ON NEWinstitution.institutionid = Person.InstitutionID
WHERE person.institutionid = #instid", lotscon))
{
cmd.Parameters.Add("#instid", SqlDbType.Int).Value = instid;
using (SqlDataAdapter adapt = new SqlDataAdapter())
{
adapt.SelectCommand = cmd;
lotscon.Open();
DataSet ds = new DataSet();
adapt.Fill(ds);
DataTable dtgenerate = new DataTable();
dtgenerate = ds.Tables[0];
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString))
{
using (SqlBulkCopy bc = new SqlBulkCopy(con))
{
bc.DestinationTableName = "tblpatient";
bc.ColumnMappings.Add("firstname", "pxfirstname");
bc.ColumnMappings.Add("lastname", "pxlastname");
bc.ColumnMappings.Add("address", "address");
bc.ColumnMappings.Add("suburb", "suburb");
bc.ColumnMappings.Add("medicareno", "medicarenumber");
bc.ColumnMappings.Add("personid", "dispenseid");
bc.ColumnMappings.Add("institutionname", "institutionname");
bc.ColumnMappings.Add("VAcardid", "repatnumber");
bc.ColumnMappings.Add("phonenumber", "phonenumber");
con.Open();
bc.WriteToServer(dtgenerate);
con.Close();
lotscon.Close();
}
}
}
}
}
}
}
}
I have a Foxpro .DBF file. I am using OLEDB driver to read the .DBF file. I can query the DBF and utilize its .CDX index file(cause it is automatically opened). My problem is that I want to query it with the .NDX index file (which is not automatically opened when the .DBF is opened). How can I open the .NDX file in C# using OLEDB driver cause the DBF is really big to search for a record without the index? Thanks all! Here is the code I am using to read the DBF.
OleDbConnection oleDbConnection = null;
try
{
DataTable resultTable = new DataTable();
using (oleDbConnection = new OleDbConnection("Provider=VFPOLEDB.1;Data Source=P:\\Test\\DSPC-1.DBF;Exclusive=No"))
{
oleDbConnection.Open();
if (oleDbConnection.State == ConnectionState.Open)
{
OleDbDataAdapter dataApdapter = new OleDbDataAdapter();
OleDbCommand command = oleDbConnection.CreateCommand();
string selectCmd = #"select * from P:\Test\DSPC-1 where dp_file = '860003'";
command.CommandType = CommandType.Text;
command.CommandText = selectCmd;
dataApdapter.SelectCommand = command;
dataApdapter.Fill(resultTable);
foreach(DataRow row in resultTable.Rows)
{
//Write the data of each record
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
try
{
oleDbConnection.Close();
}
catch (Exception e)
{
Console.WriteLine("Failed to close Oledb connection: " + e.Message);
}
}
ndx files wouldn't be opened by default and those are a thing of the past really, why wouldn't you simply add your index to your CDX. If it is not an option, then ExecScript suggestion by DRapp is what you can do. He was very close. Here is how you could do that:
string myCommand = #"Use ('P:\Test\DSPC-1') alias myData
Set Index To ('P:\Test\DSPC-1_Custom.NDX')
select * from myData ;
where dp_file = '860003' ;
into cursor crsResult ;
nofilter
SetResultset('crsResult')";
DataTable resultTable = new DataTable();
using (oleDbConnection = new OleDbConnection(#"Provider=VFPOLEDB;Data Source=P:\Test"))
{
oleDbConnection.Open();
OleDbCommand command = new OleDbCommand("ExecScript", oleDbConnection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("code", myCommand);
resultTable.Load(cmd.ExecuteReader());
oleDbConnection.Close();
}
Your connection string should only reference the PATH to where the .dbf files are located.
Then, your query is just by the table name.
new OleDbConnection("Provider=VFPOLEDB.1;Data Source=P:\\Test\\;Exclusive=No"))
selectCmd = #"select * from DSPC-1 where dp_file = '860003'";
As for using the .NDX, how / where was that created... Is that an old dBASE file you are using the Visual Foxpro driver for?
If it is a separate as described, you might need to do via an ExecScript() to explicitly open the file first WITH the index, THEN run your query. This is just a SAMPLE WITH YOUR FIXED value. You would probably have to PARAMETERIZE it otherwise you would be open to sql-injection.
cmd.CommandText = string.Format(
#"EXECSCRIPT('
USE DSPC-1 INDEX YourDSPC-1.NDX
SELECT * from DSPC-1 where dp_file = '860003'" );
Also, you might have issue with your table names being hyphenated, you may need to wrap it in [square-brackets], but not positive if it is an issue.
I am migrating my program from Microsoft SQL Server to MySQL. Everything works well except one issue with bulk copy.
In the solution with MS SQL the code looks like this:
connection.Open();
SqlBulkCopy bulkCopy = new SqlBulkCopy(connection);
bulkCopy.DestinationTableName = "testTable";
bulkCopy.WriteToServer(rawData);
Now I try to do something similar for MySQL. Because I think there would be bad performance I don't want to write the DataTable to a CSV file and do the insert from there with the MySqlBulkLoader class.
Any help would be highly appreciated.
Because I think there would be bad performance I don't want to write the DataTable to a CSV file and do the insert from there with the MySqlBulkLoader class.
Don't rule out a possible solution based on unfounded assumptions. I just tested the insertion of 100,000 rows from a System.Data.DataTable into a MySQL table using a standard MySqlDataAdapter#Update() inside a Transaction. It consistently took about 30 seconds to run:
using (MySqlTransaction tran = conn.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = conn;
cmd.Transaction = tran;
cmd.CommandText = "SELECT * FROM testtable";
using (MySqlDataAdapter da = new MySqlDataAdapter(cmd))
{
da.UpdateBatchSize = 1000;
using (MySqlCommandBuilder cb = new MySqlCommandBuilder(da))
{
da.Update(rawData);
tran.Commit();
}
}
}
}
(I tried a couple of different values for UpdateBatchSize but they didn't seem to have a significant impact on the elapsed time.)
By contrast, the following code using MySqlBulkLoader took only 5 or 6 seconds to run ...
string tempCsvFileSpec = #"C:\Users\Gord\Desktop\dump.csv";
using (StreamWriter writer = new StreamWriter(tempCsvFileSpec))
{
Rfc4180Writer.WriteDataTable(rawData, writer, false);
}
var msbl = new MySqlBulkLoader(conn);
msbl.TableName = "testtable";
msbl.FileName = tempCsvFileSpec;
msbl.FieldTerminator = ",";
msbl.FieldQuotationCharacter = '"';
msbl.Load();
System.IO.File.Delete(tempCsvFileSpec);
... including the time to dump the 100,000 rows from the DataTable to a temporary CSV file (using code similar to this), bulk-loading from that file, and deleting the file afterwards.
Similar to SqlBulkCopy, we have MySqlBulkCopy for Mysql.
here is the example how to use it.
public async Task<bool> MySqlBulCopyAsync(DataTable dataTable)
{
try
{
bool result = true;
using (var connection = new MySqlConnector.MySqlConnection(_connString + ";AllowLoadLocalInfile=True"))
{
await connection.OpenAsync();
var bulkCopy = new MySqlBulkCopy(connection);
bulkCopy.DestinationTableName = "yourtable";
// the column mapping is required if you have a identity column in the table
bulkCopy.ColumnMappings.AddRange(GetMySqlColumnMapping(dataTable));
await bulkCopy.WriteToServerAsync(dataTable);
return result;
}
}
catch (Exception ex)
{
throw;
}
}
private List<MySqlBulkCopyColumnMapping> GetMySqlColumnMapping(DataTable dataTable)
{
List<MySqlBulkCopyColumnMapping> colMappings = new List<MySqlBulkCopyColumnMapping>();
int i = 0;
foreach (DataColumn col in dataTable.Columns)
{
colMappings.Add(new MySqlBulkCopyColumnMapping(i, col.ColumnName));
i++;
}
return colMappings;
}
You can ignore the column mapping if you don't have any identity column in your table.
If you have identity column then you have to use the column mapping otherwise it won't insert any records in the table
It will just give message like "x rows were copied but only 0 rows were inserted".
This class i available in the below library
Assembly MySqlConnector, Version=1.0.0.0
Using any of BulkOperation NuGet-package, you can easily have this done.
Here is an example using the package from https://www.nuget.org/packages/Z.BulkOperations/2.14.3/
MySqlConnection conn = DbConnection.OpenConnection();
DataTable dt = new DataTable("testtable");
MySqlDataAdapter da = new MySqlDataAdapter("SELECT * FROM testtable", conn);
MySqlCommandBuilder cb = new MySqlCommandBuilder(da);
da.Fill(dt);
instead of using
......
da.UpdateBatchSize = 1000;
......
da.Update(dt)
just following two lines
var bulk = new BulkOperation(conn);
bulk.BulkInsert(dt);
will take only 5 seconds to copy the whole DataTable into MySQL without first dumping the 100,000 rows from the DataTable to a temporary CSV file.
I have a excel (.xlsm) file with stock markets live data update through the excel add-in "finansu".
When I try to read excel data through the C# application in stock name column it shows the proper stock name but at the value column it does not show any thing. That stock value column is updated live values through the finansu add-in.
Please help me out to read these values as well.
Thanks in advance.
Below is the source code:
connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fileName + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=1\";";
query = "SELECT * FROM [RealTime$]";
conn = new OleDbConnection(connString);
if (conn.State == ConnectionState.Closed)
conn.Open();
cmd = new OleDbCommand(query, conn);
da = new OleDbDataAdapter(cmd);
dt = new System.Data.DataTable();
da.Fill(dt);
OLEDB won't be able to retrieve the data you want. I'm not familiar with the finansu add-in, but Excel Interop should be able to get what you need.
Microsoft.Office.Interop.Excel
Sample code:
using Microsoft.Office.Interop.Excel;
using System;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
var filename = #"C:\temp\Book1.xlsx";
var excel = new Microsoft.Office.Interop.Excel.Application();
var wb = excel.Workbooks.Open(filename);
Worksheet realtime = wb.Sheets["RealTime"];
foreach (Range row in realtime.UsedRange.Rows)
{
Console.WriteLine("{0}", row.Cells[1, 1].Value); // Column A
Console.WriteLine("{0}", row.Cells[1, 2].Value); // Column B
// etc ...
}
wb.Close(SaveChanges: false);
}
}
}