Getting multiple MySQL Query results on Visual C# Console App - c#

I am trying to print out results from a MySQL query on a Visual C# Console application. I am able to get multiple columns on one line as you can see below, but I am wondering how I can get multiple results (rows). You see, my table holds more records that meet the query criteria. Can someone help me out?
class Program
{
static void Main(string[] args)
{
string ConnectionString = "Server=localhost; Database=world; Uid=root; Pwd=password"; // giving connection string
MySqlConnection connection = new MySqlConnection(ConnectionString);
MySqlCommand cmd = connection.CreateCommand();
cmd.CommandText = "SELECT name, population FROM city where population > 4000000";
try
{
connection.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("City name is: " + reader["name"].ToString() + " " + reader["population"].ToString());
Console.Read();
}
}

Your call to Console.Read() is blocking the while loop, so you're only printing one line to console, and then waiting for user input.
Cheers

You want to remove that Console.Read();, it is blocking your application from continuing on until it reads another character input on the Console.
Other things to consider: Using statements ensure that the unmanaged resources consumed by the MySql objects are released when the objects are no longer in use. Use Parameterized queries (aka Prepared Statements) as they performs better and are more secure.
string sql = "SELECT name, population FROM city WHERE population > #population"
using (var conn = new MySqlConnection(/*Connection String*/))
{
conn.Open();
using (var cmd = new MySqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#population", 4000000);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("City: {0} Population: {1}",
reader["name"], reader["population"]);
}
}
}
}

There may be other ways to do this, but the best way that I know of is loading the datareader into a datatable.
DataTable dt = new DataTable("City");
dt.Fill(reader);
foreach (DataRow row in dt.Rows){
Console.WriteLine("City name is: " + row["name"].ToString() + " " + row["population"].ToString());
Console.Read();
}
EDIT While answering, I realized that the Console.Read() may be the issue. This code will work, but an input to the console will need to be given for each line.

Related

How do I take sql results and populate labels in my header?

I am attempting to change my code. I am in the process of learning while I code and many parts of my C#/ASP.Net application use LINQ to SQL and many parts use commands to directly access the sql database. I felt like I should standardize these, so I am changing over the LINQ to SQL code. I have a portion of the code that takes the finds one record based on a session variable and then populates the labels in my header with the sql query results. here is the code:
protected void Page_Load(object sender, EventArgs e)
{
var pinNum = MySession.Current.focusParcel;
if (pinNum != 0)
{
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
SqlConnection connection = new SqlConnection(sql);
connection.Open();
SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM[ParcelView] WHERE PARCEL = " + pinNum, connection);
SqlDataReader selectedParcel = command.ExecuteReader();
if (selectedParcel != null)
{
lblPIN_Num.Text = selectedParcel["PARCEL"].ToString();
lblPIN_TXT.Text = selectedParcel["PIN_TXT"].ToString();
lblDateTime.Text = DateTime.Now.ToString("MMMM dd, yyyy");
lblOwner.Text = selectedParcel["Owner"].ToString();
lblAddress1.Text = selectedParcel["Address1"].ToString();
lblAddress2.Text = selectedParcel["Address2"].ToString();
lblCSZ.Text = selectedParcel["CSZ"].ToString();
lblAcres.Text = string.Format("{0} Acres", selectedParcel["ACRES"]);
lblLegal.Text = selectedParcel["LEGAL"].ToString();
if (selectedParcel["Active"].ToString() == "A")
{
lblInactive.Text = " (ACTIVE)";
}
else
{
lblInactive.Text = " (INACTIVE)";
lnkAddDocument.Visible = false;
}
}
lblCurrentUser.Text = Page.User.Identity.Name;
connection.Close();
}
else
Response.Redirect("./ParcelSearch.aspx");
}
I am receiving the following error:
Invalid attempt to read when no data is present.
I know the SQL statement used will return a record when it is used directly into sql query of database.
Thanks in advance for any advice. Should I be concerned about the lack of consistency in accessing data across the application at all? If so, should I be converting everything to LINQ? Or converting everything away from LINQ? I klnow of the existence of MVC and know I should learn it and use it, but I am too far down the path to try to convert there right now.
Change
if (selectedParcel != null)
To
if (selectedParcel.Read())
You have to read a record in the data reader before you can start pulling data. Its a forward only cursor that starts off not pointing to a record. The Read() method advances the reader by 1 and returns true/false if it was successful.
Note that it is not necessary to call HasRows if use the code above. The if block will only be entered if there is a row, if there is not then Read returns false and the block is not entered.
Edit
There are many bad practices in your code.
Use parameterized sql!! This is the most important thing you can take away here!
Wrap your connections in using blocks to ensure the connection is ALWAYS closed.
Sql code changes
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
using(SqlConnection connection = new SqlConnection(sql))
using(SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM [ParcelView] WHERE PARCEL = #pinNum", connection))
{
command.Parameters.Add(new SqlParameter("#pinNum", SqlDbType.VarChar){Value = pinNum});
connection.Open();
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
if (selectedParcel.Read())
{
/*code unchanged*/
}
}
/* 1 line code unchanged*/
}
Once you have executed the command you need to actually read the data :
SqlDataReader selectedParcel = command.ExecuteReader();
if (selectedParcel.HasRows)
{
selectedParcel.Read();
....
....
I recommend you use this format for your sqlDatareader instead, as it it will parse whether results have been retrieved or not, and will automatically dispose itself on exception:
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
while (selectedParcel .Read())
{
//Assign label text to results
}
}
You should Read() the SqlDataReader.
protected void Page_Load(object sender, EventArgs e)
{
var pinNum = MySession.Current.focusParcel;
if (pinNum == 0)
Response.Redirect("./ParcelSearch.aspx");
String sql = ConfigurationManager.ConnectionStrings["Recorder"].ConnectionString;
using(SqlConnection connection = new SqlConnection(sql))
{
connection.Open();
SqlCommand command = new SqlCommand("Select PARCEL, PIN_TXT, Owner, Address1, Address2, CSZ, ACRES, LEGAL, Active FROM[ParcelView] WHERE PARCEL =#PinNum ", connection);
//protect from sql injection
command.Parameters.AddWithValue(#PinNum, pinNum);
using(SqlDataReader selectedParcel = command.ExecuteReader())
{
if(!selectedParcel.HasRows)
return;
SetLabels(selectedParcel);
}
}
}
private void SetLabels(SqlDataReader selectedParcel)
{
lblPIN_Num.Text = selectedParcel["PARCEL"].ToString();
lblPIN_TXT.Text = selectedParcel["PIN_TXT"].ToString();
lblDateTime.Text = DateTime.Now.ToString("MMMM dd, yyyy");
lblOwner.Text = selectedParcel["Owner"].ToString();
lblAddress1.Text = selectedParcel["Address1"].ToString();
lblAddress2.Text = selectedParcel["Address2"].ToString();
lblCSZ.Text = selectedParcel["CSZ"].ToString();
lblAcres.Text = string.Format("{0} Acres", selectedParcel["ACRES"]);
lblLegal.Text = selectedParcel["LEGAL"].ToString();
if (selectedParcel["Active"].ToString() == "A")
{
lblInactive.Text = " (ACTIVE)";
}
else
{
lblInactive.Text = " (INACTIVE)";
lnkAddDocument.Visible = false;
}
lblCurrentUser.Text = Page.User.Identity.Name;
}
I refactor your code to look a little bit. You had different problems as not closing your Reader. You was open to sql injection, also you should transfer the connection to Db in separate class.

Why is my returned DataSet instance has no tables?

When I connect to the DB and I want to read data from table it doesn't work an shows these
Exception Details:
System.IndexOutOfRangeException: Cannot find table 0.
Error in Line 141
I have only one table in the dataset. Do you know the best way to read from table for me ? I return only one table. When I use foreach, how can I read a one row data?
internal DataSet read(string query)
{
DataSet nds = new DataSet();
try
{
string connectionString = "...";
try
{
SqlConnection nsqlc = new SqlConnection(connectionString);
SqlCommand get = new SqlCommand(query, nsqlc);
nsqlc.Open();
SqlDataAdapter nsda = new SqlDataAdapter(get);
nsda.Fill(nds);
nsqlc.Close();
}
catch (Exception)
{
}
return nds;
}
catch (Exception)
{
return nds;
}
}
Main form :
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
String links = "";
links = "movie%";
DataSet t = n.read("select id , poster from Movie order by id desc");
for (int i = 0; i < 10; i++)
{
links = links + t.Tables[0].Rows[i][0].ToString() + "%";
links = links + t.Tables[0].Rows[i][1] + "%";
}
The closest thing to an answer here would have to be that the n.read method returns a DataSet instance that has no tables in it. You need to step into the method with a debugger and figure out why it behaves like that.
If you see what the problem is, fix it. If not, share the read method code and someone will be able to help you further.
Looking at your updated post, I see that you are swallowing every possible exceptions that could occur while trying to fetch data. Remove the catch block from your read method. This will allow you to see what the real problem is.
Exception swallowing is something you'll want to take away from your code on a global scale. I'd suggest that you do some Googling to find out why it's a terrible habit.
About a "better way to read from table", you should consider using a IDataReader.
string links = "movie%";
using (var connection = new SqlConnection("your connection string");
{
using (var command = someExistingConnection.CreateCommand())
{
command.CommandText = "select id, poster from Movie order by id desc";
command.Connection = connection;
connection.Open();
try
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var idValue = reader.GetObject(0).ToString(); // would be better to use actual field type, which is unknown at the time I'm writing this
var posterValue = reader.GetString(1);
// perform concatenation here using the two variables declared above
links += String.Format("{0}%{1}%", idValue, posterValue);
}
}
}
finally
{
connection.Close();
}
}
}
This will outperform working with a DataSet by a long shot.

How to speed up odbc insert

So what I'm doing is reading a lot of data from remote Nettezza database and inserting them into another remote Oracle database. For that I'm using ODBC driver. The problem is that there is a lot of data and it takes too much time. How can I speed up?
Here is what I do:
First I create connection and command for inserting:
String connect = "Driver={Microsoft ODBC for Oracle};CONNECTSTRING=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=myhost)(PORT=myprt))(CONNECT_DATA=(SERVICE_NAME=myname)));Uid=uid;Pwd=pass";
connection = new OdbcConnection(connect);
connection.Open();
String q = #"INSERT INTO TEST (id)
VALUES (?)";
myCommand = new OdbcCommand(q,connection);
Then I read data from netteza:
String connect = "Driver={NetezzaSQL};servername=server;port=5480;database=db; username=user;password=pass;
string query = #"SELECT T2.LETO_MESEC, T1.*
FROM data T1
JOIN datga2 T2 ON T2.ID = T1.EFT_ID
WHERE T2.LETO_MESEC = '" + mesec + #"'";
using (OdbcConnection connection = new OdbcConnection(connect))
{
try
{
OdbcCommand command = new OdbcCommand(query, connection);
connection.Open();
OdbcDataReader reader = command.ExecuteReader();
int counter=0;
while (reader.Read())
{
int id_first = reader.GetInt32(5);
insertOracle(id_first);
}
}
catch (Exception e)
{
Console.WriteLine("ne dela" + e.ToString());
}
}
And finally my insert :
public void insertOracle(int id_first)
{
try
{
myCommand.Parameters.Clear();
myCommand.Parameters.Add(new OdbcParameter("id", id_first));
myCommand.ExecuteNonQuery();
}
catch (Exception e)
{
Console.WriteLine("ne dela" + e.ToString());
}
}
I noticed that these commit in every line, so how to remove that and speed it up. Right now it takes about 10 minutes for 20000 rows.
Single inserts are always going to be slow -- start processing the data in arrays, selecting a batch of ID's from the source system and loading an array to the target.
Here is an article which might be helpful. http://www.oracle.com/technetwork/issue-archive/2009/09-sep/o59odpnet-085168.html

SqlBulkCopy refuses to convert String.Empty into INT NULL

Recently I've been tasked with creating an automated ETL process that pumps the data into tables based on the flat file name by reading a master mapping file. I've decided to go with SqlBulkCopy and everything seemed to be fine. IDataReader interface was implemented to read flat-files, SQL Server's meta-data provided with number of columns for a one-to-one data mapping, everything was working until I ran across the file that carried empty strings. SqlBulkCopy throws an Exception saying that "The given value of type String from the data source cannot be converted to type int of the specified target column.". End of story, it does not even care that the DB type for this column is INT NULL. I know that I can interpret meta-data further, extract data types for given columns, build a DataSet based on extracted information, re-cast the data from flat-files and get myself a nice strongly typed solution that will work, but I am a lazy guy who feels like his happiness was viciously lacerated by Microsoft, or my own incompetence if someone knows of a solution to my sudden problem. Thank you for your time.
List<String> fileNames;
DateTime startJobTime = DateTime.Now;
Console.WriteLine("---------------------------------------------");
Console.WriteLine("Start Time: " + startJobTime);
Console.WriteLine("---------------------------------------------");
using (SqlConnection sqlCon = new SqlConnection(sqlConnection))
{
try
{
sqlCon.Open();
sqlCon.ChangeDatabase(edwDBName);
// Get service information for staging job
UnivStage us = GetStagingJobInfo(jobName, sqlCon);
us.StartJobTime = startJobTime;
// Get a list of file names
fileNames = GetFileList(us, args);
if (fileNames.Count > 0)
{
// Truncate Staging Table
TruncateStagingTable(us, sqlCon);
// Close and dispose of sqlCon2 connection
sqlCon.Close();
Console.WriteLine("Processing files: ");
foreach (String fileName in fileNames)
Console.WriteLine(fileName);
Console.WriteLine();
}
else
{
Console.WriteLine("No files to process.");
Environment.Exit(0);
}
// Re-open Sql Connection
sqlCon.Open();
sqlCon.ChangeDatabase(stagingDBName);
foreach (String filePath in fileNames)
{
using (SqlTransaction sqlTran = sqlCon.BeginTransaction())
{
using (FlatFileReader ffReader = new FlatFileReader(filePath, us.Delimiter))
{
using (SqlBulkCopy sqlBulkCopy =
new SqlBulkCopy(sqlCon, SqlBulkCopyOptions.Default, sqlTran))
{
SqlConnection sqlCon2 = new SqlConnection(sqlConnection);
SetColumnList(sqlCon2, us, sqlBulkCopy);
sqlBulkCopy.BatchSize = 1000;
sqlBulkCopy.DestinationTableName =
us.StagingSchemaName + "." + us.StagingTableName;
sqlBulkCopy.WriteToServer(ffReader);
sqlTran.Commit();
sqlCon2.Close();
}
}
}
}
sqlCon.ChangeDatabase(edwDBName);
sqlCon.Close();
sqlCon.Open();
SetRowCount(us, sqlCon);
sqlCon.Close();
us.EndJobTime = DateTime.Now;
sqlCon.Open();
LogStagingProcess(us, sqlCon);
sqlCon.Close();
Console.WriteLine(us.ProcessedRowCount + " rows inserted.");
Console.WriteLine("---------------------------------------------");
Console.WriteLine("Success! End Time: " + us.EndJobTime);
Console.WriteLine("---------------------------------------------");
Console.ReadLine();
}
catch (SqlException e)
{
RenderExceptionMessagesAndExit(e,
"Exception have occured during an attempt to utilize SqlBulkCopy\n");
}
}
Convert your empty strings to DBNull.

Using Multiple Data Readers

I am developing a WinForm Application in C Sharp on the .net framework.
The database string I am using as of now is
<add key="Conn" value="Data Source=MNTCON016; Database=Overtime_Calculator;Trusted_Connection=True;MultipleActiveResultSets=true" />
As I am using Microsoft SQL Server 2005 for development, I can use 2 data readers simultaneously using the MultipleActiveResultSets property to true as mentioned above.
The Method used to invoke the 2 data readers is as follows:
public static void SignUpControllerDay(DateTime Date, System.Windows.Forms.DataGridView PassedGrid)
{
string sql_SignUp = String.Format(#"SELECT Emp_ID as Emp_ID, Name as Name, Sum(Sum) as Sum FROM
(SELECT DISTINCT o.Date, e.Emp_ID as Emp_ID,
e.First_Name+ ' ' +e.Last_Name as Name,
o.Quantity as Sum
FROM Employee e,OT_Hours o,Position p,Signup_Sheet s
WHERE e.Emp_ID=o.Emp_ID
and e.Emp_ID = s.Employee_ID
and s.Day_Shift = 1
and e.Position_ID = p.Position_ID
and p.Position_Name = 'Controller'
and o.Quantity NOT IN(0.3)
and s.Date = '{0}'
and o.Date <= CONVERT(VARCHAR,'{0}',101) AND o.Date > CONVERT(VARCHAR,DATEADD(YYYY,-1,'{0}'),101) )
as OVERTIME
GROUP BY Emp_ID,Name
ORDER BY Sum", Date);
SqlConnection sqlConn = null;
SqlCommand cmd_SignUp;
SqlDataReader dr_SignUp;
try
{
sqlConn = new SqlConnection(databaseConnectionString);
sqlConn.Open();
cmd_SignUp = new SqlCommand(sql_SignUp, sqlConn);
dr_SignUp = cmd_SignUp.ExecuteReader();
while (dr_SignUp.Read())
{
ArrayList arrPhone = new ArrayList();
string sql_Phone = String.Format("SELECT Phone_Number FROM Contact_Details WHERE Emp_ID = {0}", dr_SignUp["Emp_ID"]);
SqlCommand cmd_Phone = new SqlCommand(sql_Phone, sqlConn);
SqlDataReader dr_Phone = cmd_Phone.ExecuteReader();
while (dr_Phone.Read())
{
arrPhone.Add(dr_Phone["Phone_Number"].ToString());
}
//--Retrieving Sectors
ArrayList arrSector = new ArrayList();
string sql_Sector = String.Format(#"SELECT e1.EMP_ID,
( SELECT cast(Sector_ID as varchar(10)) + ';'
FROM Employee_Sector_relationship e2
WHERE e2.Emp_ID = e1.Emp_ID
ORDER BY Sector_ID
FOR XML PATH('') ) AS Sectors
FROM Employee_Sector_Relationship e1
WHERE Emp_ID = {0}
GROUP BY Emp_ID ", dr_SignUp["Emp_ID"]);
SqlCommand cmd_Sector = new SqlCommand(sql_Sector, sqlConn);
SqlDataReader dr_Sector = cmd_Sector.ExecuteReader();
while (dr_Sector.Read())
{
arrSector.Add(dr_Sector["Sectors"].ToString());
}
if (arrSector.Count == 0)
{ arrSector.Add(" "); }
if (arrPhone.Count == 0)
{ arrPhone.Add(" "); }
//--
if (arrPhone.Count == 2)
{
PassedGrid.Rows.Add(dr_SignUp["Emp_ID"].ToString(), dr_SignUp["Name"].ToString(), arrSector[0], dr_SignUp["Sum"], arrPhone[0], arrPhone[1]);
}
else
{
PassedGrid.Rows.Add(dr_SignUp["Emp_ID"].ToString(), dr_SignUp["Name"].ToString(), arrSector[0], dr_SignUp["Sum"], arrPhone[0]);
}
}
}
catch (Exception e)
{
MessageBox.Show("Error found in SignUpControllerDay..." + Environment.NewLine + e.ToString());
}
finally
{
if (sqlConn != null)
{
sqlConn.Close();
}
}
}
Everything works fine.
Now the real problem. I have been informed that the production SQL server for the application to go live is Microsoft SQL server 2000. After doing a bit research, I came to know that Microsoft server 2000 does not support multiple active results sets propery. In short, it does not allow me to use 2 data readers simultaneously.
I need to know how to read the data from 2 different tables, simultaneously, with regards to SQL Server 2000.
Are there any other ways that i can read data as I have mentioned in the code..
Please help..
the application is almost done and is ready to go to production. but MS server 2000 doesnt allow the applcaition to work accordingly...
please help
You can have two active datareaders in Sql Server 2000 by simply creating two connections.
To demonstrate this, I must first berate you for using two very poor practices: dynamic sql and arraylists. Neither have any place in your code. You should also read up on the using construct, though you have my apologies and condolences on "using" and "arraylists" if you're still using .net 1.1.
That said, here's how the code should look:
string sql_Phone = "SELECT Phone_Number FROM Contact_Details WHERE Emp_ID = #EmpID";
using (SqlConnection cn2 = new Sqlconnection(databaseConnectionString))
using (SqlCommand cmd_Phone = new SqlCommand(sql_Phone, cn2))
{
cmd_Phone.Parameters.Add("#EmpID", SqlDbType.Int);
cn2.Open();
while (dr_SignUp.Read())
{
List<string> arrPhone = new List<string>();
cmd_Phone.Parameters[0].Value = dr_SignUp["Emp_ID"];
using (SqlDataReader dr_Phone = cmd_Phone.ExecuteReader())
{
while (dr_Phone.Read())
{
arrPhone.Add(dr_Phone["Phone_Number"].ToString());
}
}
Also, looking at your code I suspect what you really need to do is re-write your sql. You can combine all those into a single query that you just bind directly to the grid.
Sure:
public void SignUpControllerDay()
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT ...";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var phone = reader["Phone_Number"].ToString();
Bar(phone);
}
}
}
}
public void Bar(string phone)
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT ..."; // use phone to prepare statement
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// Fill the grid
}
}
}
}
You could open multiple database connections with 1 reader per connection
You could also add MultipleActiveResultSets=True; to the connection string, even though it's not recommended.
so, it is not possible! as simple as that the only answer!
multiple connection is a workaroud!
one connection can not handle multiple recordsets simultaneously

Categories