I need to write multiple queries to a single CSV file. For example I will generate a report with an Employee Schedule then below that in the same CSV I want to see the employee personal information such as salary, office location, etc. I can return both queries from a single stored procedure thinking it would write one followed by the next, but apparently that's incorrect as only the first result is returned.
My SQL Query is like the following:
SELECT EmployeeSchedule.TaskTime, Employees.EmployeeName, EmployeeSchedule.M, EmployeeSchedule.Tu, EmployeeSchedule.W,
EmployeeSchedule.Th, EmployeeSchedule.F, EmployeeSchedule.Sa, EmployeeSchedule.Su
FROM EmployeeSchedule
INNER JOIN Employees on EmployeeSchedule.EmployeeID = Employees.EmployeeID
WHERE (
EmployeeSchedule.EmployeeID = #EmployeeID AND
EmployeeSchedule.TaskTime >= #ShiftStart AND
EmployeeSchedule.TaskTime <= #ShiftEnd AND
(
(EmployeeSchedule.M=1) AND (EmployeeSchedule.M = #M) OR
(EmployeeSchedule.Tu=1) AND (EmployeeSchedule.Tu = #Tu) OR
(EmployeeSchedule.W=1) AND (EmployeeSchedule.W = #W) OR
(EmployeeSchedule.Th=1) AND (EmployeeSchedule.Th = #Th) OR
(EmployeeSchedule.F=1) AND (EmployeeSchedule.F = #F) OR
(EmployeeSchedule.Sa=1) AND (EmployeeSchedule.Sa = #Sa) OR
(EmployeeSchedule.Su=1) AND (EmployeeSchedule.Su = #Su)
)
)
ORDER BY EmployeeName, TaskTime
SELECT Employees.EmployeeName, Salary, City, AdditionalDetails
FROM EmployeeDetails
INNER JOIN Employees on EmployeeDetails.EmployeeID = Employees.EmployeeID
WHERE Employees.EmployeeID=#EmployeeID
My relevant portion of code is as follows:
public void GenerateEmployeeLog()
{
string employee = Convert.ToString(EmployeesDropDown.SelectedItem.Text);
string sqlConn = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection sqlConnection1 = new SqlConnection(sqlConn))
{
try
{
sqlConnection1.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = ("usp_" + proc);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConnection1;
cmd.Parameters.AddWithValue("EmployeeID", Convert.ToString(EmployeeDropDown.SelectedItem.Value));
cmd.Parameters.AddWithValue("ShiftStart", StartTextBox.Text);
cmd.Parameters.AddWithValue("ShiftEnd", EndTextBox.Text);
cmd.Parameters.AddWithValue("M", MCheckBox.Checked);
cmd.Parameters.AddWithValue("Tu", TuCheckBox.Checked);
cmd.Parameters.AddWithValue("W", WCheckBox.Checked);
cmd.Parameters.AddWithValue("Th", ThCheckBox.Checked);
cmd.Parameters.AddWithValue("F", FCheckBox.Checked);
cmd.Parameters.AddWithValue("Sa", SaCheckBox.Checked);
cmd.Parameters.AddWithValue("Su", SuCheckBox.Checked);
using (SqlDataReader reader = cmd.ExecuteReader())
{
try
{
using (DataTable dt = new DataTable())
{
dt.Load(reader);
using (StreamWriter writer = new StreamWriter(Response.OutputStream))
{
DataConvert.ToCSV(dt, writer, false);
Response.AddHeader("content-disposition", #"attachment;filename=""" + "EmployeeLog - " + employee + #".csv""");
Response.Charset = "";
Response.ContentType = "application/text";
Response.End();
}
}
}
catch (Exception)
{
throw;
}
finally
{
reader.Close();
reader.Dispose();
}
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConnection1.Close();
sqlConnection1.Dispose();
}
}
}
Any suggestions on how to accomplish what I'm looking for is greatly appreciated.
SOLUTION:
I ended up splitting out the two queries into different stored procedures and made the final modifications to my code, based on suggestions from the accepted answer below.
public void GenerateEmployeeLog()
{
string employee = Convert.ToString(EmployeesDropDown.SelectedItem.Text);
string sqlConn = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection sqlConnection1 = new SqlConnection(sqlConn))
{
try
{
sqlConnection1.Open();
using (SqlCommand cmd1 = new SqlCommand())
{
cmd1.CommandText = ("usp_" + proc + "_EmployeeDetails");
cmd1.CommandType = CommandType.StoredProcedure;
cmd1.Connection = sqlConnection1;
cmd1.Parameters.AddWithValue("EmployeeID", Convert.ToString(AffiliatesDropDown.SelectedItem.Value));
using (SqlDataReader reader = cmd1.ExecuteReader())
{
try
{
using (dt1 = new DataTable())
{
dt1.Load(reader);
}
}
catch (Exception)
{
throw;
}
finally
{
reader.Close();
reader.Dispose();
}
}
}
using (SqlCommand cmd2 = new SqlCommand())
{
cmd2.CommandText = ("usp_" + proc);
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Connection = sqlConnection1;
cmd2.Parameters.AddWithValue("EmployeeID", Convert.ToString(EmployeeDropDown.SelectedItem.Value));
cmd2.Parameters.AddWithValue("ShiftStart", StartTextBox.Text);
cmd2.Parameters.AddWithValue("ShiftEnd", EndTextBox.Text);
cmd2.Parameters.AddWithValue("M", MCheckBox.Checked);
cmd2.Parameters.AddWithValue("Tu", TuCheckBox.Checked);
cmd2.Parameters.AddWithValue("W", WCheckBox.Checked);
cmd2.Parameters.AddWithValue("Th", ThCheckBox.Checked);
cmd2.Parameters.AddWithValue("F", FCheckBox.Checked);
cmd2.Parameters.AddWithValue("Sa", SaCheckBox.Checked);
cmd2.Parameters.AddWithValue("Su", SuCheckBox.Checked);
using (SqlDataReader reader = cmd2.ExecuteReader())
{
try
{
using (DataTable dt2 = new DataTable())
{
dt2.Load(reader);
using (StreamWriter writer = new StreamWriter(Response.OutputStream))
{
DataConvert.ToCSV(dt2, writer, false);
writer.WriteLine();
DataConvert.ToCSV(dt1, writer, false);
Response.AddHeader("content-disposition", #"attachment;filename=""" + "EmployeeLog - " + employee + #".csv""");
Response.Charset = "";
Response.ContentType = "application/text";
Response.End();
}
}
}
catch (Exception)
{
throw;
}
finally
{
reader.Close();
reader.Dispose();
}
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConnection1.Close();
sqlConnection1.Dispose();
}
}
}
I like Mike U's 2nd approach very much, but if you absolutely must have one csv file output, its not pretty, but could you not do something like this?:
using (SqlDataReader reader = cmd.ExecuteReader()){
using (SqlDataReader reader1 = cmd1.ExecuteReader()){
using (DataTable dt = new DataTable()){
using (DataTable dt1 = new DataTable()){
dt.Load(reader);
dt1.Load(reader1);
using (StreamWriter writer = new StreamWriter(Response.OutputStream)){
DataConvert.ToCSV(dt, writer, false);
DataConvert.ToCSV(dt1, writer, false);
...
}}}}}
Judging by the Response stuff, you're doing this on a web page. The problem you're going to run into is that you can only return a single response for a single request. Since you're just sending the DataConvert.ToCSV() output as a response stream, that would mean a different stream for each of the files.
If you're not going to actually display these in HTML, then you'll need to create a ZIP file to store the two different files.
Examples for creating a multi-file ZIP file can be found here: https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile(v=vs.110).aspx
EDIT: A second option would be to emit javascript into the Page_Load event which would call an HTTPHandler which actually runs the two separate queries. This would popup two additional tabs, each with one of the CSV files. You would have to move all this code into a custom HTTPHandler, where the querystring string parameter "t" could determine which of the two queries was run.
protected void btn_OnClick(object sender, EventArgs e)
{
string sysPgLoad = "Sys.Application.add_load(function () {{ {0}; }});";
this.Page.ClientScript.RegisterStartupScript(this.Page.GetType(), "employeeLog", string.Format(sysPgLoad, "window.Open('exployeeCsv.axd?t=log')"));
this.Page.ClientScript.RegisterStartupScript(this.Page.GetType(), "employeeSal", string.Format(sysPgLoad, "window.Open('exployeeCsv.axd?t=sal')"));
}
Related
I have to copy 500k rows of bulk data to excel using c# code.I get this data as output from a stored procedure in 7 parts. Every time I get the data, I am immediately copying it using response. write and I get the excel in the end. However I am facing the issue of duplicate rows getting inserted in the excel. I am working with vs2008. I am not sure if i can use openxml for this. I have tried using temporary datatables and collating them and later copying them to excel. It was giving the same duplicate entries.
My code:
public void GetReport(string CommandText, int StartIndex)
{
DataTable dt = new DataTable();
using (SqlConnection cn = new SqlConnection(GetConnectionString()))
{
using (SqlCommand command = new SqlCommand())
{
// setup command
command.Connection = cn;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = CommandText;
// params
command.Parameters.Add(new SqlParameter("#application_id_array", SqlDbType.VarChar, 100));
command.Parameters[0].Value = _applicationCSV;
command.Parameters.Add(new SqlParameter("#startIndex", SqlDbType.Int, 4));
command.Parameters[1].Value = StartIndex * _divisor + 1;
command.Parameters.Add(new SqlParameter("#maxRowCount", SqlDbType.Int, 4));
command.Parameters[2].Value = _divisor;
command.Parameters.Add(new SqlParameter("#region", SqlDbType.Int, 4));
command.Parameters[3].Value = _region;
command.CommandTimeout = 60;
// open connection
cn.Open();
SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
try
{
while (reader.Read())
{
Response.Output.Write("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}\t{16}\n", reader["ID"].ToString(), reader["description"].ToString(),
reader["User_Name"].ToString(),
reader["country_code"].ToString(),
reader["ref_indicator"].ToString(),
reader["department"].ToString(),
reader["employee_indicator"].ToString(),
reader["phone"].ToString(),
reader["application_name"].ToString(),
reader["application_user_logon"].ToString(),
reader["role"].ToString(),
reader["ParentTask"].ToString(),
reader["application_name2"].ToString(),
reader["application_user_logon2"].ToString(),
reader["role2"].ToString(),
reader["conflict_task_name"].ToString(),
reader["Exclusion"].ToString());
}
}
catch (Exception e)
{
throw e;
}
}
}
}
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
int pages = _reportRowCount / _divisor + 1;
Response.Clear();
Response.Charset = "";
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("content-disposition", "attachment;filename=report.xls");
#region using loops
for (int i = 0; i < pages; i++)
{
if (i == 0)
{
Response.Write("Id\tCategory\tUser Name\tCountry Code\tReference Indicator\tDepartment\tEmployee Indicator\tPhone\tApp Name A\tUser ID A\tApp Role A\tSOD Task Group A\tApp Name B\tUser ID B\tApp Role B\tSOD Task Group B\tApplicable Exclusion\n");
}
this.GetReport("sp_GetPaged" + GetStoredProcedureSuffix(), i);
}
Response.End();
#endregion
}
The _reportRowCount value is the count of the total number if rows which is usually around 500k. _divisor value is a constant=65535.
First post here. I'm trying to create a website that fetches data from an Oracle database and returns some tables. I was able to connect my database fine and made a DataConnector that returns a list of CodeDesc objects. My main problem right now is simply displaying that data to the screen, preferably in the form of a drop down list but I'm using a GridView for now.
Here's my front end:
protected void Button1_Click(object sender, EventArgs e)
{
DataConnector dc = new DataConnector();
GridView2.DataSource = dc.getCodeTypes();
GridView2.DataBind();
}
When I click the button, nothing is generated and the debugger only says "Exception thrown: 'System.ArgumentException' in Oracle.DataAccess.dll" Any help would be appreciated. This is my first time doing web development and it's been a struggle to get even this far. I'm using Visual Studio 2015
Back End:
//Create datatable to get info from db & return results
public List<CodeDesc> getCodeTypes()
{
try
{
OracleConnection con = new OracleConnection(connString);
con.Open();
string query = "select id, descr from code_desc where code_type_id = 0";
// Create the OracleCommand
OracleCommand cmd = new OracleCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
// Execute command, create OracleDataReader object
OracleDataReader reader = cmd.ExecuteReader();
List<CodeDesc> L = new List<CodeDesc>();
while (reader.Read())
{
CodeDesc c = new CodeDesc();
c.id = reader.GetInt32(0);
c.description = reader.GetString(1);
L.Add(c);
}
// Clean up
reader.Dispose();
cmd.Dispose();
con.Dispose();
System.Diagnostics.Debug.WriteLine(L);
return L;
}
catch (Exception ex)
{
// catch clause here...
}
}
CodeDesc:
public class CodeDesc
{
public int id { get; set; }
public string description { get; set; }
}
Any help would be great.
You never set the query string to the CommandText property of the OracleCommand. Of course this can only result in an exception when you try to execute your command.
Said that, remember that every disposable object should be enclosed in a using statement. This is very important in case of exceptions because the correct closing and disposing is executed automatically exiting from the using block
public List<CodeDesc> getCodeTypes()
{
try
{
List<CodeDesc> L = new List<CodeDesc>();
string query = "select id, descr from code_desc where code_type_id = 0";
using(OracleConnection con = new OracleConnection(connString))
using(OracleCommand cmd = new OracleCommand(query, con))
{
con.Open();
// Execute command, create OracleDataReader object
using(OracleDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
CodeDesc c = new CodeDesc();
c.id = reader.GetInt32(0);
c.description = reader.GetString(1);
L.Add(c);
}
}
}
System.Diagnostics.Debug.WriteLine(L);
return L;
}
This question is continuation of my previous one. Without going into too much details, I'm filling dataset with 2 related 1-to-many tables.
So, my question now is - why this code works good
public DataAgencyR_DataSet SelectOne(int id)
{
DataAgencyR_DataSet result = new DataAgencyR_DataSet();
using (DbCommand command = Connection.CreateCommand())
{
try
{
command.CommandText = SqlStrings.SelectDataAgencyR_SelectOne();
var param = ParametersBuilder.CreateByKey(command, "ID_DeclAgenc", id, "ID_DeclAgenc");
command.Parameters.Add(param);
Connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
System.Diagnostics.Trace.WriteLine(String.Format("{0}-{1}", reader[0], reader[1]));
}
System.Diagnostics.Trace.WriteLine("-------------");
reader.NextResult();
while (reader.Read())
{
System.Diagnostics.Trace.WriteLine(String.Format("{0}-{1}", reader[0], reader[1]));
}
}
}
catch (DbException e)
{
Logger.Error(e.Message, e);
throw new DataAccessException("Error occurs while SelectOne method porcessed", e);
}
finally
{
if (Connection.State != ConnectionState.Closed) Connection.Close();
}
}
return result;
}
public static string SelectDataAgencyR_SelectOne()
{
return "SELECT a.* FROM t0_DataAgency_R a WHERE a.SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc;" +
"SELECT c.* FROM t01_ChoiceParam_R c JOIN t0_DataAgency_R a on a.ID_DeclAgenc = c.ID_DeclAgenc WHERE SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc";
}
and this is not
public DataAgencyR_DataSet SelectOne(int id)
{
DataAgencyR_DataSet result = new DataAgencyR_DataSet();
using (DbCommand command = Connection.CreateCommand())
{
try
{
command.CommandText = SqlStrings.SelectDataAgencyR_SelectOne();
var param = ParametersBuilder.CreateByKey(command, "ID_DeclAgenc", id, "ID_DeclAgenc");
command.Parameters.Add(param);
Connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
result.t0_DataAgency_R.Load(reader);
reader.NextResult();
result.t01_ChoiceParam_R.Load(reader);
}
}
catch (DbException e)
{
Logger.Error(e.Message, e);
throw new DataAccessException("Error occurs while SelectOne method porcessed", e);
}
finally
{
if (Connection.State != ConnectionState.Closed) Connection.Close();
}
}
return result;
}
public static string SelectDataAgencyR_SelectOne()
{
return "SELECT a.* FROM t0_DataAgency_R a WHERE a.SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc;" +
"SELECT c.* FROM t01_ChoiceParam_R c JOIN t0_DataAgency_R a on a.ID_DeclAgenc = c.ID_DeclAgenc WHERE SetToPartners = 1 AND a.ID_DeclAgenc = #ID_DeclAgenc";
}
After second example, I have filled only result.t0_DataAgency_R table - but not result.t01_ChoiceParam_R. Why can it be so?
Thanks in advance
DataTable.Load automatically advances the reader to the next result. So you should remove your explicit call to NextResult.
Meaning:
using (DbDataReader reader = command.ExecuteReader())
{
result.t0_DataAgency_R.Load(reader);
result.t01_ChoiceParam_R.Load(reader);
}
Adding a DataSet to the mix... we used to use SqlDataAdapter and returned a DataSet but didn't take advantage of any of the offline features, etc., so a SqlDataReader is a better fit. Here's code to fill a DataSet. Found this was about 10% faster overall.
Dim s As DataSet = New DataSet()
Using reader As SqlDataReader = command.ExecuteReader()
Dim tables As New List(Of DataTable)
Do
Dim table As New DataTable()
table.Load(reader)
tables.Add(table)
s.Tables.Add(table)
Loop While Not reader.IsClosed
s.Load(reader, LoadOption.OverwriteChanges, tables.ToArray())
End Using
I'm trying to do something like this:
public void ImportClick(object sender, EventArgs e) //the button used after selecting the spreadsheet file
{
if (fileUpload.HasFile) //ASP.Net FileUpload control
{
if (fileUpload.FileName.EndsWith(".xls", StringComparison.OrdinalIgnoreCase) || fileUpload.FileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
{
Excel sheet = new Excel(fileUpload.Open()); //not sure how to do this part
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["our_database"].ConnectionString))
{
using (SqlCommand command = new SqlCommand("INSERT INTO table_name SELECT * FROM " + sheet, connection))
{
connection.Open();
command.ExecuteQuery(); //Intellisense only has "ExecuteNonQuery()" method, but that's for T-SQL
connection.Close();
}
}
}
else
{
error.Text = "File must be either *.xls or *.xlsx";
error.Visible = true;
}
}
else
{
error.Text = "No file was selected";
error.Visible = true;
}
}
There are a lot of classes and interfaces in the Microsoft.Office.Interop.Excel namespace, and I don't know which one to use.
I know making the Excel object, along with the SQL command, probably won't be as easy as what I have here, but those are the two things I need help with.
Any suggestions/advice would be greatly appreciated!
I would suggest using Microsoft Jet Engine.
private static void UploadExcelToDB(string p)
{
try
{
using (SqlConnection conn = new SqlConnection(DBConnString))
{
conn.Open();
if (conn.State == ConnectionState.Open)
{
Log("Opened connection to DB");
}
SqlBulkCopy sbk = new SqlBulkCopy(conn);
sbk.BulkCopyTimeout = 600;
sbk.DestinationTableName = DbTableName;
DataTable excelDT = new DataTable();
OleDbConnection excelConn = new OleDbConnection(ExcelConnString.Replace("xFILEx",p));
excelConn.Open();
if (excelConn.State == ConnectionState.Open)
{
Log("Opened connection to Excel");
}
OleDbCommand cmdExcel = new OleDbCommand();
OleDbDataAdapter oda = new OleDbDataAdapter();
cmdExcel.CommandText = "SELECT * FROM ["+ExcelTableName+"]";
cmdExcel.Connection = excelConn;
oda.SelectCommand = cmdExcel;
oda.Fill(excelDT);
if (excelDT != null)
{
Log("Fetched records to local Data Table");
}
excelConn.Close();
SqlCommand sqlCmd = new SqlCommand("TRUNCATE TABLE ICN_NUGGET_REPORT_RAW",conn);
sqlCmd.CommandType = CommandType.Text;
Log("Trying to clear current data in table");
int i = sqlCmd.ExecuteNonQuery();
Log("Table flushed");
Log("Trying write new data to server");
sbk.WriteToServer(excelDT);
Log("Written to server");
conn.Close();
}
}
catch (Exception ex)
{
Log("ERROR: " + ex.Message);
SendErrorReportMail();
}
finally
{
#if (DEBUG)
{
}
#else
{
string archive_file = ArchiveDir+"\\" + DateTime.Now.ToString("yyyyMMdd-Hmmss") + ".xlsx";
File.Move(p, archive_file);
Log("Moved processed file to archive dir");
Log("Starting archive process...");
}
#endif
}
}
This is how ExcelConnString looks like:
public static string ExcelConnString { get { return "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=xFILEx;Extended Properties=\"Excel 12.0;HDR=YES\";";} }
HDR=YES - this means that if you have column names in spreadsheet it will be treated as target table column names for matching each other.
I am thinking of creating an instance of excel.application class and writing codes to loop through the cells. And using SQL insert query to copy the rows one by one to the SQL table. I'm still working it out anyway and would paste the code when i'm done.
i got out of memory exception upon loading image from file.. i retrieve the file from database and the output file image can be viewed..
String temp = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "/img.jpg";
using (FileStream fs = new FileStream(temp, FileMode.Create))
{
fs.Write(StudentImage(id), 0, StudentImage(id).Length);
fParent.picAvatar.BackgroundImage = Image.FromFile(temp);
//fParent.picAvatar.Image = Image.FromFile(temp2);
}
private byte[] StudentImage(String _id)
{
try
{
String sqlCmd = String.Format("SELECT studpic FROM dbo.studentpic WHERE idnum = '{0}'", _id);
using (SqlConnection con = new SqlConnection(gVar._conString))
{
con.Open();
SqlCommand cmd = new SqlCommand(sqlCmd, con);
using (SqlDataReader r = cmd.ExecuteReader())
{
r.Read();
byte[] imgData = (byte[])r["studpic"];
return imgData;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
did i miss any object that needs to close?
You tend to get an OutOfMemory exception when an image cannot be read for a number of reasons.
In your case the issue is that you have not closed your file! Also, you are calling the database twice.
I suggest the following code:
File.WriteAll(temp, StudentImage(id));
fParent.picAvatar.Image = Image.FromFile(temp);