I created an list named 'PTNList', and everything I needed added to it just fine. Now I am attempting to write code to retrieve each element from that list and run it against an SQL query. I have a feeling I'm not sure exactly how to about this. The CompareNumbers.txt file generates, but nothing is printed to it. Any help is greatly appreciated.
Below is the section of code I believe needs to be worked with.
using (FileStream fs = new FileStream("c:/temp/CompareNumbers.txt", FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
foreach (var ptn in PTNList)
{
//create sql for getting the count using "ptn" as the variable thats changing
//call DB with sql
//Get count from query, write it out to a file;
Console.WriteLine("Running Query");
string query2 = #"SELECT COUNT(PRODUCT_TYPE_NO)
AS NumberOfProducts
FROM dbo.PRODUCT
Where PRODUCT_TYPE_NO = " + ptn;
SqlCommand cmd2 = new SqlCommand(query2);
cmd2.Connection = con;
rdr = cmd2.ExecuteReader();
while (rdr.Read())
{
sw.WriteLine(rdr["NumberOfProducts"]);
}
rdr.Close();
}
You haven't used apostrophes around the values. But you should use parameters anyway. You could use one query instead of one for every type. For example with this approach:
string sql = #"SELECT COUNT(PRODUCT_TYPE_NO) AS NumberOfProducts
FROM dbo.PRODUCT
Where PRODUCT_TYPE_NO IN ({0});";
string[] paramNames = PTNList.Select(
(s, i) => "#type" + i.ToString()
).ToArray();
string inClause = string.Join(",", paramNames);
using (SqlCommand cmd = new SqlCommand(string.Format(sql, inClause)))
{
for (int i = 0; i < paramNames.Length; i++)
{
cmd.Parameters.AddWithValue(paramNames[i], PTNList[i]);
}
// con.Open(); // if not already open
int numberOfProducts = (int) cmd.ExecuteScalar();
}
Update: maybe you really just want to loop them and get their count. Then you don't need this complex approach. But you should still use sql-parameters to prevent sql-injection and other issues like missing apostrophes etc.
You'll want to convert the column back to a type, e.g.
sw.WriteLine(rdr["NumberOfProducts"] as string);
Also, note that your query is prone to SqlInjection attacks and should be parameterized, and that SqlCommand is also disposable. You can squeeze a bit more performance by reusing the SqlCommand:
string query2 = #"SELECT COUNT(PRODUCT_TYPE_NO)
AS NumberOfProducts
FROM dbo.PRODUCT
Where PRODUCT_TYPE_NO = #ptn";
using (var cmd2 = new SqlCommand(query2))
{
cmd2.Connection = con;
cmd2.Parameters.Add("#ptn", SqlDbType.Varchar);
foreach (var ptn in PTNList)
{
cmd2.Parameters["#ptn"].Value = ptn;
Console.WriteLine("Running Query");
using var (rdr = cmd2.ExecuteReader())
{
if (rdr.Read())
{
sw.WriteLine(rdr["NumberOfProducts"] as string);
}
}
}
}
Are you sure your query give an result and sw.WriteLine is executed? I would redesign your code like this, becfause if you have an error in your data query, you might get into trouble. I always like to use this (schema):
IDataReader reader = null;
try
{
// create every thing...
}
catch(Exception ex)
{
// catch all exceptions
}
finally()
{
if(reader != null)
{
reader.Close();
}
}
And use the same for your connection, so that you can be sure, it is closed correct.
Related
I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}
I have a very silly problem. I am doing a select, and I want that when the value comes null, return an empty string. When there is value in sql query, the query occurs all ok, but if there is nothing in the query, I have to give a sqlCommand.CommandTimeout greater than 300, and yet sometimes gives timeout. Have a solution for this?
public string TesteMetodo(string codPess)
{
var vp = new Classe.validaPessoa();
string _connection = vp.conString();
string query = String.Format("SELECT COUNT(*) FROM teste cliente WHERE cod_pess = {0}", codPess);
try
{
using (var conn = new SqlConnection(_connection))
{
conn.Open();
using (var cmd = new SqlCommand(query, conn))
{
SqlDataReader dr = cmd.ExecuteReader();
if(dr.HasRows)
return "";
return codPess;
}
}
}
You should probably validate in the UI and pass an integer.
You can combine the usings to a single block. A bit easier to read with fewer indents.
Always use parameters to make the query easier to write and avoid Sql Injection. I had to guess at the SqlDbType so, check your database for the actual type.
Don't open the connection until directly before the .Execute. Since you are only retrieving a single value you can use .ExecuteScalar. .ExecuteScalar returns an Object so must be converted to int.
public string TesteMetodo(string codPess)
{
int codPessNum = 0;
if (!Int32.TryParse(codPess, out codPessNum))
return "codPess is not a number";
var vp = new Classe.validaPessoa();
try
{
using (var conn = new SqlConnection(vp.conString))
using (var cmd = new SqlCommand("SELECT COUNT(*) FROM teste cliente WHERE cod_pess = #cod_pess", conn))
{
cmd.Parameters.Add("#cod_pess", SqlDbType.Int).Value = codPessNum;
conn.Open();
int count = (int)cmd.ExecuteScalar();
if (count > 0)
return "";
return codPess;
}
}
catch (Exception ex)
{
return ex.Message;
}
}
I'm trying to make sure that both my insert and delete below work completely or not at all. I have my connection object outside of my transaction scope which I believe is correct by not 100% sure.
I do know that this code is not working as I intent. After the first part (the insert runs) and then I abort by terminating on a break point, the rows are indeed inserted even though I never called scope.complete.
Please point out the flaw in my thinking and logic here.
sqlConnection.Open();
int numFound = 1;
int max = 99;
int iteration = 0;
while (iteration < max && numFound > 0)
{
iteration++;
var ids = new List<int>();
using (var sqlCommand0 = new SqlCommand(sql0, sqlConnection))
{
using (SqlDataReader reader1 = sqlCommand0.ExecuteReader())
{
while (reader1.Read())
{
ids.Add(reader1.GetInt32(0));
}
}
}
numFound = ids.Count;
if (numFound > 0)
{
using (var scope = new TransactionScope())
{
string whereClause = $"WHERE Id IN ({string.Join(",", ids)})";
string sql1 = string.Format(sqlTemplate1, whereClause);
using (var sqlCommand1 = new SqlCommand(sql1, sqlConnection))
{
sqlCommand1.ExecuteNonQuery();
}
// BREAK POINT HERE - ABORTED PROGRAM AND sql1 had been committed.
var sql2 = "DELETE FROM SendGridEventRaw " + whereClause;
using (var sqlCommand2 = new SqlCommand(sql2, sqlConnection))
{
sqlCommand2.ExecuteNonQuery();
}
scope.Complete();
total += numFound;
Console.WriteLine("deleted: " + whereClause);
}
}
}
}
I think it's because you open your connection before starting your transaction. You could try to fix your issue by first starting your transaction and then opening your connection.
Just from what I am seeing and from what I am assuming is what you intend to happen is this:
If your first query gets some records, then the next query executes, hence the statement:
if (numFound > 0)
If that is the case, and where you put your breakpoint is true, of course the insert statement will fire. Reason is:
using (var sqlCommand1 = new SqlCommand(sql1, sqlConnection))
{
sqlCommand1.ExecuteNonQuery();
}
is within that if statement. You're saying "if there are any rows, execute the insert query."
If you're trying to actually get the scope object to do the query, then you're going to have to have all of the query construction happening within the object and then having scope.complete() doing the execution.
For example:
//In TransactionScope class
public string Complete(var ids, int numFound, SqlConnection sqlConnection, string sqlTemplate1)
{
string whereClause = $"WHERE Id IN ({string.Join(",", ids)})";
string sql1 = string.Format(sqlTemplate1, whereClause);
using (var sqlCommand1 = new SqlCommand(sql1, sqlConnection))
{
sqlCommand1.ExecuteNonQuery();
}
var sql2 = "DELETE FROM SendGridEventRaw " + whereClause;
using (var sqlCommand2 = new SqlCommand(sql2, sqlConnection))
{
sqlCommand2.ExecuteNonQuery();
}
return whereClause;
}
//in your Main class
if (num > 0)
{
string whereClause = scope.Complete(ids, numFound, sqlConnection, sqlTemplate1);
Console.WriteLine("deleted" + whereClause"." );
}
I am of course just going off of the assumptions I stated above. If I am incorrect, please let me know.
Hope it helps.
I am trying to populate a group of labels in a C# windows form with some values that are in a certain attribute (PlayerName) in a database that I have in access.
Currently the only code I have is:
OleDbConnection connection = new OleDbConnection(CONNECTION STRING HERE);
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "SELECT PlayerName FROM [TotalPlayerName] WHERE Team = 1 AND SportID = " + Form1.IDNumber;
I need a list or array that holds these values so I can use them to populate the labels, but I am unaware of how to do this.
You need to call ExecuteReader to obtain a data reader and then loop through the rows of the result set like this:
List<string> result = new List<string>();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
result.Add(reader.GetString(0));
}
}
Before you do this, don't forget to open the connection like this:
connection.Open();
There are a couple of things here..... for sake of best practice well its more standard practice... as I like to say!
Use USING as this cleans up after connection.. see here for great examples in a "using" block is a SqlConnection closed on return or exception?
using (OdbcDataReader DbReader = DbCommand.ExecuteReader())
{
int fCount = DbReader.FieldCount;
while (DbReader.Read())
{
Label1 = DbReader.GetString(0);
Label2 = DbReader.GetString(1);
Label3 = DbReader.GetString(2);
Label4 = DbReader.GetString(3);
for (int i = 0; i < fCount; i++)
{
String col = DbReader.GetString(i);
Console.Write(col + ":");
}
Console.WriteLine();
}
}
NB your SQL only return 1 field /String at the moment
while reading the data fill the list like
List<string> players = new List<string>();
OleDbDataReader rdr = command.ExecuteReader();
While(rdr.Read())
{
players.Add(rdr["PlayerName"].ToString());
}
You need to create a OleDbReader object to read the response from the query. You will also need to create a List to store the data Something like this:
List<string> playerNameList = new List<string>();
using (OleDbReader r = command.ExecuteReader())
{
while(reader.Read())
{
playerNameList.Add(reader.GetString(0));
}
}
One option might be using OleDbDataAdapter to fill a DataTable those values that returns your query;
var dt = new DataTable();
using(var da = new OleDbDataAdapter(command))
{
da.Fill(dt);
}
And since your query return one column, you can use AsEnumerable to that datatable to get them as a string like;
List<string> list = dt.AsEnumerable()
.Select(r => r.Field<string>("PlayerName"))
.ToList();
You can read: Queries in LINQ to DataSet
By the way, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Also use using statement to dispose your connection and command automatically as I did for OleDbDataAdapter in my example.
I made function in c# to read line by line and then load lines to sqlite (s3db).
private void LoadFromDictionary()
{
Encoding enc = Encoding.GetEncoding(1250);
using (StreamReader r = new StreamReader("c:\\Temp2\\dictionary.txt", enc))
{
string line = "";
while ((line = r.ReadLine()) != null)
{
line = line.Trim();
AddWord(line);
}
}
MessageBox.Show("Finally :P", "Info");
}
private void AddWord(string w)
{
String insSQL = "insert into Words values(\"" + w + "\")";
String strConn = #"Data Source=C:\Temp2\dictionary.s3db";
SQLiteConnection conn = new SQLiteConnection(strConn);
SQLiteDataAdapter da = new SQLiteDataAdapter(insSQL, strConn);
da.Fill(dt);
dataGridView1.DataSource = dt.DefaultView;
}
But is it any faster way? I created table by sqlite administrator application.
Can sqlite load itself file and make it as a table?
I am talking about 3+ millions words (one word in one line).
PS. please correct my topic if there is something wrong :)
Yes, there is a much, much faster method using the following techniques:
1) Only open a connection to the database one time
2) Use a parameterized command for better performance and lower overhead (don't have to use new strings on each pass).
3) Wrap the entire operation in a transaction. As a general rule, this will improve your performance.
Note that I do not show transaction rollback or closing the connection, which are also best practices that should be implemented.
private void LoadFromDictionary()
{
Encoding enc = Encoding.GetEncoding(1250);
string strConn = #"Data Source=C:\Temp2\dictionary.s3db";
SqliteConnection conn = new SqliteConnection(strConn);
conn.Open();
string insSQL = "insert or ignore into wyrazy values(#Word)";
DbCommand oCommand = conn.CreateCommand();
oCommand.Connection = conn;
oCommand.CommandText = insSQL;
DbParameter oParameter = oCommand.CreateParameter();
oParameter.Name = "#Word";
oParameter.DbType = DbType.String;
oParameter.Size = 100;
oCommand.Parameters.Add(oParameter);
DbTransaction oTransaction = conn.BeginTransaction();
using (StreamReader r = new StreamReader("c:\\Temp2\\dictionary.txt", enc))
{
string line = "";
while ((line = r.ReadLine()) != null)
{
line = line.Trim();
if (!string.IsNullOrEmpty(line)) {
oParameter.Value = line;
oCommand.ExecuteNonQuery();
}
}
}
oTransaction.Commit();
conn.Close();
MessageBox.Show("Finally :P", "Info");
}
You could try bulk insert. By reading this article please pay special attention to the parametrized queries being used there and which you should use instead of the string concatenations in your sample in the insSQL variable.
Using Transactions usually speed things up quite a bit, depending on your desired batch size. I'm not 100% as familiar with DataAdapters and DataSources but instead of creating a new connection every time to insert one row, modify your code to use one connection and use SQLiteConnection.BeginTransaction() and the when you are done call Transaction.Commit().
I just did this the other day, first use a transaction, and parameterized queries. I was able to load 16 million rows in about a minute doing this.
internal static void FastInsertMany(DbConnection cnn)
{
using (DbTransaction dbTrans = cnn.BeginTransaction())
{
using (DbCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = "INSERT INTO TestCase(MyValue) VALUES(?)";
DbParameter Field1 = cmd.CreateParameter();
cmd.Parameters.Add(Field1);
for (int n = 0; n < 100000; n++)
{
Field1.Value = n + 100000;
cmd.ExecuteNonQuery();
}
}
dbTrans.Commit();
}
}