Saving results from SELECT query to another table - c#

I have a SQL query generates the results found below it using Visual Studio query window:
SELECT SUM(TaskLength) AS TaskLength
FROM myTable
WHERE EventStartTime BETWEEN '2019/8/17' AND '2019/8/19'
GROUP BY TaskName
ORDER BY TaskLength
The query results window produces the following:
TaskName TaskLength
--------------------------
Idle Time 20
Start Shift 31
Downtime 85
Engineering 120
Part Prep 141
Maintenance 172
Production 417
My C# code below only returns one string representing one column from one row.
using (SqlConnection con = new SqlConnection(_connectionString))
{
string query = #"SELECT SUM(TaskLength) AS TaskLength
FROM myTable
WHERE EventStartTime BETWEEN '2019/8/17' AND '2019/8/19'
GROUP BY TaskName
ORDER BY TaskLength";
using (SqlCommand cmd = new SqlCommand(query, con))
{
con.Open();
object result = cmd.ExecuteScalar();
string totalMinutes = Convert.ToString(result);
}
}
Does the returned cmd.ExecuteScalar object represent the entire resulting table?
If not, is there another way to do this. My thoughts are to save the results to an intermediate table (if it does not already do this) and save that to a .csv file there unless there is a better way.

ExecuteScalar returns only the first column from the first row.
If you have many rows to read then you need to call ExecuteReader and loop on the using the value returned by the SqlDataReader.Read until it returns false.
While looping you store the results of your query in some kind of List creating objects matching the info returned by the reader.
Then, this list can be easily used as DataSource for some kind of User Interface object like a DataGridView or similar or used to write your data into a file or other storage.
// The model that describes the data returned by the query
public class TaskData
{
public string Name {get;set;}
public int Length {get;set;}
}
.....
// Where you store each record retrieved by the query
List<TaskData> results = new List<TaskData>();
using (SqlConnection con = new SqlConnection(_connectionString))
{
// Added the taskname to the query
string query = #"SELECT TaskName, SUM(TaskLength) as TaskLength
FROM myTable
WHERE EventStartTime
BETWEEN '2019/8/17' AND '2019/8/19'
GROUP BY TaskName ORDER BY TaskLength";
using (SqlCommand cmd = new SqlCommand(query, con))
{
con.Open();
// Get a reader to loop over the results
using(SqlDataReader reader = cmd.ExecuteReader())
{
// Start reading data (until there are no more records to read)
while(reader.Read())
{
// From the current record build the TaskData info
TaskData data = new TaskData
{
Name = reader["TaskName"].ToString(),
Length = Convert.ToInt32(reader["TaskLength"]);
}
// Add this info the the collection of your results
results.Add(data);
}
}
}
}
And now, if you want to store the result to a CSV file, the code is simply
File.WriteAllLines("yourFile.csv",
string.Join(Environment.NewLine,
results.Select(x => x.Name +","+x.Length))

Related

Check if a database table contains any rows

I'm loading data into a form with 3 Entry controls.
The object I am using for this is called mySettings, which is an object of SystemSettings, a class and database table in my SQLite database.
So far I have this code, and it works as is.
var db = new SQLiteConnection(dbPath);
Entry txtServer;
txtServer = new Entry { FontSize = 10 };
controlGrid.Children.Add(txtServer, 2, 0);
Grid.SetColumnSpan(txtServer, 4);
SystemSettings mySettings;
mySettings = db.Get<SystemSettings>(0);
txtServer.Text = mySettings.FTPServer;
However, I need to check whether SystemSettings contains any rows in the table before I load values in.
I've seen a few guides online.
Some say use something along the lines of
SQLiteCommand cmd;
cmd = new SQLiteCommand(db);
...
int result = Convert.ToInt32(db.ExecuteScalar)
However, I get an error there saying
SQLiteCommand does not contain any method containing x parameters
no matter how many I pass in (0 or more).
There also doesn't appear to be a method as part of db.
So how can I check whether SystemSettings contains any rows, before trying to use data that doesn't exist?
The pattern below should work. The .ExecuteScalar() method is actually on the command and not the connection.
int count;
using (SQLiteConnection db = new SQLiteConnection("MY_CXN_STRING"))
using (SQLiteCommand cmd = new SQLiteCommand("SELECT COUNT(*) FROM SystemSettings"))
{
db.Open();
count = (int)cmd.ExecuteScalar();
db.Close();
}
bool hasRows = count != 0;
Basically you want to clear
SystemSettings
Try just running a query that returns nothing against the database. For instance:
SystemSettings = $"SELECT * FROM TABLE_NAME WHERE COLUMN_NAME IS 'INVALID_EXPRESSIONdjeiq48724rufnjdrandom stuff'";
Not the most elegant solution by any means, but it works.
What you want to do is to get the first row in you SystemSettings table if any:
You should therefore execute the following Sql Statement (or something similar) and check if a result is returned:
Select * from SystemSettings LIMIT 1;
You can execute the query and check the result like this:
public bool DoesTableContainRows(string tableName, SQLiteConnection connection)
{
var command = new SQLiteCommand($"Select * from {tableName } LIMIT 1;", connection);
var resultReader = command.ExecuteReader();
// check whether or not a row was returned
bool containRows = resultReader.Read();
resultReader.Close();
return containRows;
}
Edit:
Shows how to check if a table contains rows using .NET and Microsoft.Data.Sqlite including better disposing of resources.
public bool DoesTableContainRows(string tableName, SqliteConnection connection)
{
using (var command = new SqliteCommand($"Select * from {tableName } LIMIT 1;", connection))
{
using (var resultReader = command.ExecuteReader())
{
// check whether or not a row was returned
bool containRows = resultReader.Read();
resultReader.Close();
return containRows;
}
}
}

Fetch Mysql data with Coma after each fetch C# Mysql

I am trying to fetch the data from mysql data base Column where say i have multiple rows data for specific column and i need to include coma after each row fetch.
before it was giving the data when i tried to add coma
Current Output after adding code Response.Write(name.Split(','));
System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]
My DB
Phone_Number School_id
1 SC1
2 SC1
3 SC1
4 SC1
Expected Output
1,2,3,4
My Fetch Query
string constr = ConfigurationManager.ConnectionStrings["Logging"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
using (MySqlCommand MySqlCommand = new MySqlCommand("SELECT Phone_Number FROM Login where SchoolId='" + SessionManager.SchoolId + "'", con))
{
MySqlCommand.CommandType = CommandType.Text;
con.Open();
MySqlDataReader MySqlDataReader = MySqlCommand.ExecuteReader();
while (MySqlDataReader.Read())
{
string name = MySqlDataReader["FatherFullName"].ToString();
Response.Write(name.Split(','));
}
con.Close();
}
}
String.Split method splits your string with special character or string. This is not what you want.
You can add your Phone_Number values to a List<string> and you can use string.Join(string, IEnumerable<String>) method to generate comma separated values.
var list = new List<string>();
while(MySqlDataReader.Read())
{
string name = MySqlDataReader["FatherFullName"].ToString();
if(!string.IsNullOrEmpty(name))
{
list.Add(name);
}
}
Response.Write(string.Join(",", list)); // 1,2,3,4
You should always use parameterized queries by the way. This kind of string concatenations are open for SQL Injection attacks.
Two things more;
Default value CommandType is Text. You don't need to assign it explicitly.
You don't need to close your connection with con.Close(). Since you used using statement, it does that automatically.
String.Split is used to
split a string into substrings based on the strings in an array
You can use String.Join like this:-
//define a list of string
List<string> phoneNumbers = new List<string>();
while (MySqlDataReader.Read())
{
//add all the phone numbers to the list
string phoneNum = MySqlDataReader["FatherFullName"].ToString();
if(!String.IsNullOrEmpty(phoneNum))
phoneNumbers.Add(phoneNum);
}
//finally use Join method to get expected result
Response.Write(String.Join(",",phoneNumbers));
Also, please note your query is open for SQL Injection attack and you should consider using paramaterized query instead.
Next to the split/join, you can also get the result from MySQL directly:
SELECT GROUP_CONCAT(Phone_Number) FROM Login WHERE SchoolId = 'id';
This returns 1 single row with all phone numbers, seperated by comma's.

How can I retrieve the query definition (SQL text) of an Access query and store back a changed definition

I have a requirement where I need to read queries from Access DB in c# and check if the access db query has any keyword like "KEY" if it has keywords I need to enclose that in square brackets"[]".just like how it is done in SQL.
Could someone suggest me how to do that?
You can retrieve the query text like this:
string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\...\myDB.mdb";
using (var conn = new OleDbConnection(connString )) {
conn.Open();
string[] restrictions = new string[] { null, null, "myQuery" };
DataTable schema = conn.GetSchema("Views", restrictions);
if (schema.Rows.Count > 0) {
DataRow row = schema.Rows[0];
string queryText = (string)row["VIEW_DEFINITION"];
Console.WriteLine(queryText);
}
}
If you drop the restrictions argument with the query name, conn.GetSchema("Views") returns one row for each query. If you query conn.GetSchema("Procedures") other types of queries like insert, update and DDL statements that are not considered as queries are returned in row["PROCEDURE_DEFINITION"].
View (query) names are returned in row["TABLE_NAME"] and procedure names in row["PROCEDURE_NAME"].
And you can update the query like this:
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var cmd = new OleDbCommand("DROP PROCEDURE myQuery", conn);
cmd.ExecuteNonQuery();
cmd = new OleDbCommand("CREATE PROCEDURE myQuery AS SELECT * FROM myTable", conn);
cmd.ExecuteNonQuery();
}
Strangely enough the OleDb CREATE DDL (Data Definition Language) designates the queries as 'procedures' but the schema table returns a 'VIEW_DEFINITION' and the query name is returned in the column 'TABLE_NAME'. SELECT queries must be retrieved as "Views", other types of queries as "Procedures"; however, both types are created as PROCEDUREs.
While I was testing the answer that #Olivier Jacot-Descombes provided, I was not able to retreive all the queries text representation. Therefore I applied some other method where you open the existing Ms Access database instance and read the queries that are stored in it.
Here is the class I used:
public class MsAccess
{
private Microsoft.Office.Interop.Access._Application _oAccess;
public MsAccess(string path)
{
_oAccess = (Microsoft.Office.Interop.Access._Application)System.Runtime.InteropServices.Marshal.BindToMoniker(path);
}
public string ReturnSqlQueryText(string queryName)
{
string queryDef = null;
var qdefs = _oAccess.CurrentDb().QueryDefs;
foreach (QueryDef qdef in qdefs)
{
if(qdef.Name.Equals(queryName))
queryDef = qdef.SQL;
}
return queryDef;
}
}
Using this code might require you adding using Microsoft.Office.Interop.Access.Dao and Microsoft.Office.Interop.Access both (15.0.0.0) where you can find them under Extension on the reference menu

Using Retrieved Data from a Parameterized SQL Query

If I'm using a parameterized query (ASP.NET with C#) in SQL, such as
var db = Database.Open("Database1");
SqlCommand cmd = new SqlCommand("SELECT * FROM pageinfo WHERE pageID = #pageID");
cmd.Parameters.AddWithValue("#pageID", 1);
And later on in the page, I want to do a foreach loop of whatever data was retrieved:
foreach(var row in ???)
What would I use (in place of the ???) to access the data I just retrieved?
Thanks.
It depends on how you execute a query.
Usually it's done by SqlCommand.ExecuteReader
For example, in your case, you can:
....
SqlDataReader reader = cmd .ExecuteReader();
while (reader.Read())
{
...
}
But there are also other ways to rertieve the data, for example using DataSet
For a complete example on how to do that can have a look on:
Using ADO.NET for beginners
You can use while iteration statement with SqlCommand.ExecuteReader instead of foreach. Take a look this;
var db = Database.Open("Database1");
SqlCommand cmd = new SqlCommand("SELECT * FROM pageinfo WHERE pageID = #pageID");
cmd.Parameters.AddWithValue("#pageID", 1);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader[0]);
}
reader[0] returns first row of first column and reader[1] returns first row of second column if your data has of course.

Populating an object based on a one-to-many table relationship in SQL

I have an object in C# like this:
private ClassWidget
{
public int ID;
public List<int> WidgetFavoriteNumbers;
}
Let's say I have two tables in SQL, one defines widget properties, and the other holds many records for a single widget, let's say the widget's favorite numbers:
widgets
-----------
id (int, not null)
// other properties ...
widget_nums
----------
widget_id (int, not null)
num (int)
I find myself frequently executing two SQL queries to populate this object even though I know I can join the tables to create just one query. The reason is that it seems simpler to populate the object with just the data I need rather than iterating over result sets that have a lot of duplicate data. Of course this widget example is much simplified compared to the real scenario. Here's the example:
int WidgetID = 8;
ClassWidget MyWidget = new ClassWidget();
using (SqlConnection conn = GetSQLConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = #"SELECT id FROM widgets WHERE id = #WidgetID;";
cmd.Parameters.AddWithValue("WidgetID", WidgetID);
using (SqlDataReader Reader = cmd.ExecuteReader())
{
if (Reader.HasRows)
MyWidget.ID = GetDBInt("id", Reader); // custom method to read database result
}
cmd.CommandText = #"SELECT num FROM widget_nums WHERE widget_id = #WidgetID;";
using (SqlDataReader Reader = cmd.ExecuteReader())
{
if (Reader.HasRows)
while (Reader.Read())
MyWidget.WidgetFavoriteNumbers.Add(GetDBInt("num", Reader));
}
conn.Close();
}
}
My question is whether I should continue using this type of approach, or if performing a table join would be recommended. If the table join is recommended, what is the best design pattern to populate the object? My problem is that I have to create some logic to filter out duplicate rows, and is especially complicated when I am getting all widgets rather than just one.
I would use a table join. It is pretty simple to create a method which will traverse the results. You can use this method even when querying for multiple widgets and and their widget_nums
private IEnumerable<ClassWidget> MapReaderToWidget(IDataReader reader) {
var dict = new Dictionary<int, ClassWidget>();
while (reader.Read()) {
var id = (int)reader["id"];
ClassWidget widget;
if (!dict.TryGetValue(id, out widget)) {
widget = new ClassWidget {
ID = id,
WidgetFavoriteNumbers = new List<int>();
};
dict.Add(id, widget);
}
widget.WidgetFavoriteNumbers.Add((int)reader["num"]);
}
return dict.Values;
}
Then rewrite your method as following:
using (SqlConnection conn = GetSQLConnection())
{
using (SqlCommand cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = #"SELECT id FROM widgets INNER JOIN widget_nums on .... WHERE id = #WidgetID;";
cmd.Parameters.AddWithValue("WidgetID", WidgetID);
using (SqlDataReader Reader = cmd.ExecuteReader()) {
return MapReaderToWidget(reader).FirstOrDefault();
}
}
}
Use the table join. It uses a single SQL query, and it's extremely fast (far faster than your current approach). And for logic to filter out duplicate rows, you can come up with a query for that, I'd imagine; take some time to develop a query that gives you what you want out of the database, and you'll be pleased with the results.
I think you should start moving to Ado Entity Framework or LinQ to SQL as you data provideer as it will save you a lot of time and it will do exactly what you want in an efficient way.

Categories

Resources