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.
Related
Can anyone help improve performance? Updating the table takes a lot of time.
I am updating the serial number from datagridview to a table called dbo.json
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (string.IsNullOrEmpty(row.Cells[5].Value as string))
{
}
else
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
var cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
using (var con = new SqlConnection(cnn))
{
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
MessageBox.Show("dbo.json is ready");
}
You shouldn't create the connection and command inside such a tight loop - create and open the connection and command ONCE before the loop, and in the loop, only set the parameter values and execute the query for each entry.
Something like this:
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
string VAL1;
string VAL2;
// define connection string, query text *ONCE* before the loop
string cnn = ConfigurationManager.ConnectionStrings["sql"].ConnectionString;
string updateQuery = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2;";
// create connection and command *ONCE*
using (SqlConnection con = new SqlConnection(cnn))
using (SqlCommand cmd = new SqlCommand(updateQuery, cnn))
{
// Define parameters - adapt as needed (don't know the actual datatype they have)
cmd.Parameters.Add("#VAL1", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#VAL2", SqlDbType.VarChar, 100);
// open connection ONCE, for all updates
con.Open();
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
{
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
{
for (int i = 0; i <= DgvWhistlSorted.Rows.Count - 2; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
// set the values
cmd.Parameters["#VAL1"].Value = VAL1;
cmd.Parameters["#VAL2"].Value = VAL2;
// execute query
cmd.ExecuteNonQuery();
}
}
}
// close connection after all updates are done
con.Close();
}
MessageBox.Show("dbo.json is ready");
}
Create the connection ONCE...you're creating a new database connection each time through the loop! And in fact you do not need to create new command objects each time. You can reuse the command object because the parameters are the same. Just clear the params each time through the loop.
Also don't do the grid view count in the loop, set a variable for it.
string query = "UPDATE dbo.json SET RowN = #VAL1 WHERE [A-order] = #VAL2";
int counter = DgvWhistlSorted.Rows.Count - 2;
using (SqlConnection con = new SqlConnection(cnn))
{
con.Open();
using(SqlCommand cmd = new SqlCommand(cnn,query))
{
cmd.Parameters.Clear();
//Do your loop in here
for (int i = 0; i <= counter; i++)
{
VAL1 = DgvWhistlSorted.Rows[i].Cells[6].Value.ToString();
VAL2 = DgvWhistlSorted.Rows[i].Cells[0].Value.ToString();
cmd.Parameters.AddWithValue("#VAL1", VAL1);
cmd.Parameters.AddWithValue("#VAL2", VAL2);
cmd.ExecuteNonQuery();
}
}
}
A better idea is to do this in one command, by passing all the data in a Table-Value Parameter (TVP):
First create the table type. I don't know your data types, so I'm guessing here. Make sure to match the types to the existing table.
CREATE TYPE dbo.OrderJson (
Order int PRIMARY KEY,
RowN nvarchar(max) NOT NULL
);
Then you can pass the whole thing in one batch. You need to create a DataTable to pass as the parameter, or you can use an existing datatable.
// UPDATE dbo.json with numbers
private void BtnUpdateSql_Click(object sender, EventArgs e)
{
var table = new DataTable {
Columns = {
{ "Order", typeof(int) },
{ "RowN", typeof(string) },
},
};
foreach (DataGridViewRow row in DgvWhistlSorted.Rows)
if (!string.IsNullOrEmpty(row.Cells[5].Value as string))
table.Rows.Add(DgvWhistlSorted.Rows[i].Cells[0].Value, DgvWhistlSorted.Rows[i].Cells[6].Value)
const string query = #"
UPDATE dbo.json
SET RowN = t.RowN
FROM dbo.json j
JOIN #tmp t ON t.order = j.[A-order];
";
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["sql"].ConnectionString))
using (var cmd = new SqlCommand(query, con))
{
cmd.Parameters.Add(new SqlParameter("#tmp", SqlDbType.Structured) { Value = table, TypeName = "dbo.OrderJson" });
con.Open();
cmd.ExecuteNonQuery();
}
MessageBox.Show("dbo.json is ready");
}
I found that the fastest way would be to save the DATAGRIDVIEW to an SQL table and continue the process with - stored procedure + update query - between two tables - now it flies ...
Thank you all
All, can someone assist.
I have problems while reading a BLOB object in SQL DB. The file download but cannot open due to file being corrupted. Error appears with any document type (pdf, docx, jpg, etc).
The code executes a Stored Procedure which fetches the binary based on two different parameters.
Here is the code:
protected void Page_Load(object sender, EventArgs e)
{
// Request.QueryString["docid"].ToString();
//string DocumentID = Request.QueryString["DocumentID"].ToString();
string DocumentID = "9163736c-8064-11e8-ab16-2c44fd826130";
string SessionId = "91494483-8064-11e8-ab16-2c44fd826130";
//Connection and Parameters
SqlParameter param1 = null;
SqlParameter param2 = null;
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings["ProcessManagerConnectionString"].ToString());
SqlCommand cmd = new SqlCommand("sp_getdoc", conn);
cmd.CommandType = CommandType.StoredProcedure;
param1 = new SqlParameter("#DocumentID", SqlDbType.NVarChar, 100);
param2 = new SqlParameter("#SessionId", SqlDbType.NVarChar, 100);
param1.Direction = ParameterDirection.Input;
param2.Direction = ParameterDirection.Input;
param1.Value = DocumentID;
param2.Value = SessionId;
cmd.Parameters.Add(param1);
cmd.Parameters.Add(param2);
//Open connection and fetch the data with reader
conn.Open();
SqlDataReader reader =
cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (reader.HasRows)
{
reader.Read();
//
string doctype = reader["Extension"].ToString();
string docname = reader["Docname"].ToString();
//
Response.Buffer = false;
Response.ClearHeaders();
Response.ContentType = doctype;
Response.AddHeader("Content-Disposition",
"attachment; filename=" + docname);
//
//Code for streaming the object while writing
const int ChunkSize = 1024;
byte[] buffer = new byte[ChunkSize];
byte[] binary = (reader["Data"]) as byte[];
MemoryStream ms = new MemoryStream(binary);
int SizeToWrite = ChunkSize;
for (int i = 0; i < binary.GetUpperBound(0) - 1; i = i + ChunkSize)
{
if (!Response.IsClientConnected) return;
if (i + ChunkSize >= binary.Length)
SizeToWrite = binary.Length - i;
byte[] chunk = new byte[SizeToWrite];
ms.Read(chunk, 0, SizeToWrite);
Response.BinaryWrite(chunk);
Response.Flush();
}
Response.Close();
}
No need to do any chunking here. You already have the binary data loaded into memory. Just make a single call to:
Response.BinaryWrite(binary);
Avoid creating MemoryStream and whatnot. I have a feeling there is a bug with your chunking code.
If you are looking to reduce the memory usage in your app when streaming binary data, you should look into using CommandBehavior.SequentialAccess.
Lastly, I prefer to handle this type of thing using a simple System.Web.IHttpHandler (ASHX) over an System.Web.UI.Page (ASPX).
Try creating a file called HectorsHandler.ashx with the following content:
<%# WebHandler Language="C#" Class="HectorsApp.HectorsHandler" %>
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web;
namespace HectorsApp
{
public class HectorsHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctxt)
{
// Request.QueryString["docid"].ToString();
//string DocumentID = Request.QueryString["DocumentID"].ToString();
string DocumentID = "9163736c-8064-11e8-ab16-2c44fd826130";
string SessionId = "91494483-8064-11e8-ab16-2c44fd826130";
//Connection and Parameters
SqlParameter param1 = null;
SqlParameter param2 = null;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ProcessManagerConnectionString"].ToString()))
{
SqlCommand cmd = new SqlCommand("sp_getdoc", conn);
cmd.CommandType = CommandType.StoredProcedure;
param1 = new SqlParameter("#DocumentID", SqlDbType.NVarChar, 100);
param2 = new SqlParameter("#SessionId", SqlDbType.NVarChar, 100);
param1.Direction = ParameterDirection.Input;
param2.Direction = ParameterDirection.Input;
param1.Value = DocumentID;
param2.Value = SessionId;
cmd.Parameters.Add(param1);
cmd.Parameters.Add(param2);
//Open connection and fetch the data with reader
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
if (reader.Read())
{
//
string doctype = reader["Extension"].ToString();
string docname = reader["Docname"].ToString();
//
ctxt.Response.BufferOutput = false;
ctxt.Response.Buffer = false;
ctxt.Response.ContentType = doctype;
ctxt.Response.AddHeader("Content-Disposition", "attachment; filename=" + docname);
//Code for streaming the object while writing
byte[] buffer = new byte[8040];
long dataIndex = 0;
while (ctxt.Response.IsClientConnected)
{
long bytesRead = reader.GetBytes(reader.GetOrdinal("Data"), dataIndex, buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
ctxt.Response.OutputStream.Write(buffer, 0, (int)bytesRead);
ctxt.Response.OutputStream.Flush();
dataIndex += bytesRead;
}
}
}
}
}
public bool IsReusable
{
get { return false; }
}
}
}
Per the comments below, there is no longer need to clear any data since you now have full control over what is being output.
Note that because CommandBehavior.SequentialAccess is being used, columns must be read in order.
Is there any performance gain from CommandBehavior.SequentialAccess?
I have a stored procedure that would count the number of times a value is in a database. However in the code behind for loop when it reaches
(int)command.ExecuteScalar(); the loop stops and asks to supply parameter for #invoice2.
Stored Procedure:
select count(*) as CountInvoice where invoice1=#invoice1 or #invoice2=#invoice`2
Code Behind:
using (SqlCommand command = new SqlCommand("sp_Count", conn))
{
foreach (TextBox textBox in placehldr1.Controls.OfType<TextBox>())
{
count += 1;
command.CommandType = CommandType.StoredProcedure;
string invoice = textBox.Text.TrimEnd();
string parameter = string.Format("#invoice{0}", count);
command.Parameters.AddWithValue(parameter, invoice);
int invoiceCount = (int)command.ExecuteScalar();
if (invoiceCount > 0)
{
lblError.Text = "Invoice number already exist";
return;
}
command.Parameters.Clear();
}
Would something like this work better as it factors in the two parameters.
select count(*) as CountInvoice where invoice1=#invoice1 or invoice2=#invoice2
using (SqlCommand command = new SqlCommand("sp_Count", conn))
{
foreach (TextBox textBox in placehldr1.Controls.OfType<TextBox>())
{
count1 += 1;
count2 += 1;
command.CommandType = CommandType.StoredProcedure;
string invoice = textBox.Text.TrimEnd();
string parameter1 = string.Format("#invoice1", count1);
string parameter2 = string.Format("#invoice2", count2);
command.Parameters.AddWithValue(parameter, invoice);
int invoiceCount = (int)command.ExecuteScalar();
if (invoiceCount > 0)
{
lblError.Text = "Invoice number already exist";
return;
}
command.Parameters.Clear();
}
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')"));
}
I want to print the output of the stored procedure in a .csv file.
When I insert a single stored procedure such as exec spGet Table 5 1,null,null,null,111,null,null,null,61,null,null,3;
Along with its parameters it executes. But when I pass the same procedure multiple times with different parameters, It only executes the first Stored procedure and the remaining are ignored. In the CSV file i only get the first SP Output.
My code is as follows
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnGetSku_Click(object sender, EventArgs e)
{
Stopwatch swra = new Stopwatch();
swra.Start();
StreamWriter CsvfileWriter = new StreamWriter(#"D:\testfile.csv");
string connectionString = null;
SqlConnection cnn;
connectionString = "Data Source=My-PC-Name;Initial Catalog=MyDB;User
cnn = new SqlConnection(connectionString);
ID=Name;Password=********";
cnn.Open();
SqlCommand cmd = new SqlCommand(textBox1.Text, cnn);
cmd.CommandText = textBox1.Text;
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 2000;
using (cnn)
{
using (SqlDataReader rdr = cmd.ExecuteReader())
using (CsvfileWriter)
{
//For getting the Table Headers
DataTable Tablecolumns = new DataTable();
for (int i = 0; i < rdr.FieldCount; i++)
{
Tablecolumns.Columns.Add(rdr.GetName(i));
}
CsvfileWriter.WriteLine(string.Join(",",
Tablecolumns.Columns.Cast<DataColumn>().Select(csvfile =>
csvfile.ColumnName)));
while (rdr.Read())
{
label1.Text = rdr["SKU"].ToString() + " " +
rdr["SKUCode"].ToString();
CsvfileWriter.WriteLine(rdr["SKU"].ToString() + "," +
rdr["SKUCode"].ToString() + "," +
rdr["Compliance_Curr"].ToString() + "," +
rdr["Compliance_Prev"].ToString() + "," +
rdr["Difference"].ToString() + "," +
rdr["TotalSales_Curr"].ToString() + ",");
}
cnn.Close();
}
}
swra.Stop();
Console.WriteLine(swra.ElapsedMilliseconds);
}
}
I want to make sure that each procedure is executed differently and appended to the .csv file.
The problem you are facing is due to overwriting the file each time. So what you have in there is actually the result of the last execution of the command.
The culprit is the following line:
StreamWriter CsvfileWriter = new StreamWriter(#"D:\testfile.csv");
According to documentation,
If the file exists, it is overwritten; otherwise, a new file is created.
You need to use an overload of StreamWriter constructor which accepts a bool value specifying whether to append to the file or overwrite it.
var csvFileWriter = new StreamWriter(#"D:\testfile.csv", true);
Have you tried to call stored procedure with names of parameters (which declared in stored procedure)?
For example: EXECUTE spGet #Id = 1, #Number = 111, #....
In your code, you are creating StreamWriter object **everytime, you are clicking on **btnGetSku, Try to make it member variable and then write. Data is not being appended,
StreamWriter CsvfileWriter = null;
private void btnGetSku_Click(object sender, EventArgs e)
{
Stopwatch swra = new Stopwatch();
swra.Start();
if(CsvfileWriter == null)
CsvfileWriter = new StreamWriter(#"D:\testfile.csv");
string connectionString = null;
SqlConnection cnn;
connectionString = "Data Source=My-PC-Name;Initial Catalog=MyDB;User
cnn = new SqlConnection(connectionString);
ID=Name;Password=********";
cnn.Open();
SqlCommand cmd = new SqlCommand(textBox1.Text, cnn);
cmd.CommandText = textBox1.Text;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2000;
using (cnn)
{
using (SqlDataReader rdr = cmd.ExecuteReader())
// Don't use using here. This disposes the streams
//using (CsvfileWriter)
{
//For getting the Table Headers
DataTable Tablecolumns = new DataTable();
for (int i = 0; i < rdr.FieldCount; i++)
{
Tablecolumns.Columns.Add(rdr.GetName(i));
}
CsvfileWriter.WriteLine(string.Join(",",
Tablecolumns.Columns.Cast<DataColumn>().Select(csvfile =>
csvfile.ColumnName)));
while (rdr.Read())
{
label1.Text = rdr["SKU"].ToString() + " " +
rdr["SKUCode"].ToString();
CsvfileWriter.WriteLine(rdr["SKU"].ToString() + "," +
rdr["SKUCode"].ToString() + "," +
rdr["Compliance_Curr"].ToString() + "," +
rdr["Compliance_Prev"].ToString() + "," +
rdr["Difference"].ToString() + "," +
rdr["TotalSales_Curr"].ToString() + ",");
}
cnn.Close();
}
}
swra.Stop();
Console.WriteLine(swra.ElapsedMilliseconds);
}