I want to read a relatively large table from an MDB file and save its fields to a Dictionary. This is a one time operation and I will not need to UPDATE or INSERT INTO database after that. So I only need a SELECT query.
What is the fastest way to do this. Using Datasets seems slow:
var con = new OleDbConnection();
const string dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;";
const string dbSource = "Data Source = D:/programming/Frames.mdb";
con.ConnectionString = dbProvider + dbSource;
con.Open();
const string query = "SELECT * FROM [Concrete Design 1 - Column Summary Data - ACI 318-99]";
var dt = new DataTable();
var da = new OleDbDataAdapter(query, con);
da.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
// Create Dictionary here
}
I know using the below method will be faster but I just don't know how to get it right:
var con = new OleDbConnection();
const string dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;";
const string dbSource = "Data Source = D:/programming/Frames.mdb";
con.ConnectionString = dbProvider + dbSource;
var cmd = new OleDbCommand { Connection = con };
con.Open();
cmd.CommandText = "SELECT * FROM [Concrete Design 1 - Column Summary Data - ACI 318-99]";
// I don't know how get the fields and rows from the database
con.Close();
Probably the most efficient approach is using a DataReader to stream the data into the Dictionary. You can use DataReader.FieldCount to get the number of fields.
Here is an example using a custom class that holds all fields:
var dict = new Dictionary<object, Record>();
using(var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
object key = reader[0];
Record rec = new Record(key);
for(int i=0; i< reader.FieldCount; i++)
{
rec.Fields.Add(reader[i]);
}
dict.Add(key, rec);
}
}
Here's the class, use the correct types and multiple properties instead of the list if possible:
public class Record
{
public Record(object key)
{
this.Key = key;
Fields = new List<object>();
}
public object Key;
public List<object> Fields;
}
I think you should check if a datareader with a loop while reader read is faster to add item to your dictionnary.
Related
I need to return all values from my table. How to write a code inside while?
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// my doubt is here
}
return list;
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// **** read column name data from table ****
string Id = (string)dr["Id"];
string company = (string)dr["company"];
string city = (string)dr["City"];
var objItem = new { Id = Id, company = company, city = "city" };
list.Add(objItem);
}
return list;
You have several options. 1 - load DataSet from OracleDataReader. There you will have all your data.
2 - you can still use select *... but you need a model. Then create List<SomeModel> instead of List<dynamic> with
while (reader.Read())
{
model.Property = reader["columnName"]; // will need convert type and take care of DB null. Can use existing extnsions
. . . .
}
3 - For arbitrary number of columns use OracleDataReader.FieldCount and some storage like List<object[]>
var data = new List<object[]>();
var fCnt = reader.FieldCount;
while (reader.Read())
{
var arr = new Object[fCnt];
for(int i = 0; i < fCnt; i++)
arr[i] = reader[i];
data.Add(arr);
}
The unfortunate part with #3 is that in the end you can get jagged array and not 2-dimentional one but you now have enough info to convert it. But I don't remember when I needed to do #3. So think about #1 and #2
And one more thing - absolutely no need for dynamic here. Stay away.
I have put together the following method:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
}
return valuesList;
}
I'd like to be able to run an assertion like this one:
var a = DbQueryToArry();
Assert.IsTrue(a.Contains("some value"));
Given reader [0]
valuesList.Add(Convert.ToString(reader[0]));
I only get the first column (CLIENTINFO) into the array and not the second (ACCOUNT_Purpose). How should I modify the code to get both ?
In addition, the returned values could be either a String or Int so would my current code version should be handling both ?
Thanks in advance.
If we switch from obsolete ArrayList to something like IEnumerable<T>:
public static IEnumerable<IDataRecord> DbQueryToArray(string sql) {
if (null == sql)
throw new ArgumentNullException(nameof(sql));
//TODO: do not hardcode connetcion string but read it (say, from Settings)
string SqlCString = "connString";
//DONE: Wrap IDisposable into using
using (SqlConnection connection = new SqlConnection(SqlCString)) {
connection.Open();
//DONE: Wrap IDisposable into using
using (SqlCommand command = new SqlCommand(sql, connection)) {
//DONE: Wrap IDisposable into using
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
yield return reader as IDataRecord;
}
}
}
}
}
then you can use Linq in order to query the result:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT");
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["CLIENTNO"]) == "some value"));
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["ACCOUNT_Purpose"]) == "some other value"));
If you don't want execute query several times, you can materialize the results:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.ToList();
Assert.IsTrue(a.Any(record => Convert.ToString(record[0]) == "some value"));
Assert.IsTrue(a.Any(record => Convert.ToString(record[1]) == "some other value"));
Finally (see comments below), if we want to test if any field in any record has the value:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.SelectMany(line => {
// Flatten the cursor into IEnumerable<String>
string[] result = new string[line.FieldCount];
for (int i = 0; i < result.Length; ++i)
result[i] = Convert.ToString(line[i]);
return result;
});
a.Any(item => item == "some value");
It is because you only read the first value of the reader. Reader.Read() read each row one by one and Convert.ToString(reader[0])) means that you want to read the first column as string.
That's cause you are getting only the first column. You can do something like below by specifying the column name
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader["CLIENTNO"]));
valuesList.Add(Convert.ToString(reader["ACCOUNT_Purpose"]));
}
Moreover, since you are converting all the columns to string; I would suggest to use a strongly typed collection like List<string> rather then ArrayList valuesList = new ArrayList();
The other answers are good, However some concerns
Lets stop using ArrayList, and use a List<T> instead
Lets use a using statement where you can
Note : I have used a ValueTuple to return more than 1 field
Example
public static List<(string clientNo, string account)> DbQueryToArray()
{
const string SqlCString = "connString";
var valuesList = new List<(string clientNo, string account)>();
using (var connection = new SqlConnection(SqlCString))
{
connection.Open();
using (var command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection))
{
var reader = command.ExecuteReader();
while (reader.Read())
valuesList.Add(((string)reader[0],(string)reader[1]) );
}
}
return valuesList;
}
Usage
var results = DbQueryToArray();
Assert.IsTrue(results.Any(x => x.clientNo == someValue || x.account == someValue));
best practice is to check first if the reader has rows
reader.HasRows
then close the reader and the connection
your code should look like this:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
using (connection)
{
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
valuesList.Add(Convert.ToString(reader[1])); // add to valuelist
}
}
reader.Close(); // close reader
} //dispose connection
return valuesList;
}
Use DataTable and SqlDataAdapter to get query result in form of table. Something like this:
string connString = #"your connection string here";
string query = "select * from table";
DataTable dataTable = new DataTable();
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
Then you can use dataTable object to see if particular values exist.
After an hour of debugging I am trying to find why my Query returns only 1 ID where there are at least 3:
public static string[] selectAGIdOfKC(string id)
{
int nbAg = 0;
DataTable results = new DataTable();
using (OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=bdd.mdb;
Persist Security Info=False;"))
{
OleDbCommand cmd = new OleDbCommand("SELECT Id FROM ActionG WHERE Num_Kc = #Id", conn);
conn.Open();
cmd.Parameters.AddWithValue("#Id", id);
OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
conn.Close();
adapter.Fill(results);
nbAg=results.Rows[0].ItemArray.Count();
}
string[] myTab = new string[nbAg];
for (int i = 0; i < nbAg; i++)
{
myTab[i] = results.Rows[0].ItemArray[i].ToString();
}
return myTab;
}
I tried to use some workarounds but nothing solves the problem of the missing returned IDs...
Can anyone help me ? Thanks for your time!
It all depends on how you want the results, but assuming that you want nbAg to contain the number of rows returned, and assuming that you want myTab to contain the id-values returned, your code is not written to do what you want.
Please note the ellipsis ... - I have only modified the code that was causing your current symptoms.
public static string[] selectAGIdOfKC(string id)
{
int nbAg = 0;
...
nbAg = results.Rows.Count();
string[] myTab = new string[nbAg];
for (int i = 0; i < nbAg; i++)
{
myTab[i] = results.Rows[i][0].ToString();
// This is the first column of row i
}
return myTab;
}
In my database table, I have a column name 'SectionDatatable' which are in XML datatype. In my C# code, after I have connection to database to my database and I make a query to get SectionDatatablewhich is XML format in my database, UserDefinedSectionData. I need to convert the 'SectionDatatable' in XML Datatype and convert it into DataSet, how can I do it. I have stuck for 1 day, the following is my code.
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
csb.DataSource = #"CPX-XSYPQKSA91D\SQLEXPRESS";
csb.InitialCatalog = "DNN_Database";
csb.IntegratedSecurity = true;
string connString = csb.ToString();
string queryString = "select * FROM UserDefinedSectionData WHERE SectionDataTabId = #tabId";
using (SqlConnection connection = new SqlConnection(connString))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = queryString;
command.Parameters.Add(new SqlParameter("tabId", tabId));
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string sectionData= reader["SectionDatatable"].ToString();
int moduleId = Int32.Parse(reader["SectionDataModuleId"].ToString());
}
}
}
This is the simple example of converting XML string to DataSet. This sample also demonstrates processing all tables in DataSet.
You need to replace XML string in this sample with your XML output from database. You can change code as per you need to access data.
string RESULT_OF_SectionDatatable = "<note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>";
var xmlReader = XmlReader.Create(new StringReader(RESULT_OF_SectionDatatable));
DataSet ds = new DataSet();
ds.ReadXml(xmlReader);
foreach (DataTable table in ds.Tables)
{
Console.WriteLine(table);
Console.WriteLine();
foreach (var row in table.AsEnumerable())
{
for (int i = 0; i < table.Columns.Count; ++i)
{
Console.WriteLine(table.Columns[i].ColumnName +"\t" + row[i]);
}
Console.WriteLine();
}
}
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.