I'm trying to write a method that returns a list with every row that a select query returns. But anything I can find on this is based on a single table.
This is what I'm trying:
public List<string> Select(string querystring)
{
string query = "SELECT " + querystring;
List<string> results = new List<string>();
if (this.OpenConnection())
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
// in here I don't want a to "hard code" every column
results.Add( "returned data" );
}
dataReader.Close();
this.CloseConnection();
return results;
}
else
{
return results;
}
}
Is this possible? Do I need to make a new method for every table?
Or should I maybe return a list of objects instead of strings?
I'm really having a brainfart here so any help is appreciated.
As I stated from my comment:
I'm not sure what you're trying to do exactly... it looks like you're hoping to put a rows and columns (your data set) into just rows (your list of string). How are you planning on using this data? It will not be very useable trying to use it in the manner it looks like you're attempting. You can reference your data readers columns by index, but again I'm not clear on what you're hoping to accomplish.
You can however do it by doing something like this:
while (reader.Read())
{
// for loop with a maximum iteration of the number of columns in the reader.
for (int i=0;i<reader.FieldCount;i++)
{
results.Add(reader[i].ToString());
}
}
but again, I don't think this will get you data in a useable manner.
I would recommend you to look into Dapper.NET or EF6 or nHibernate.
Related
I have a list Called ListTypes that holds 10 types of products. Below the store procedure loops and gets every record with the product that is looping and it stores it in the list ListIds. This is killing my sql box since I have over 200 users executing this constantly all day.
I know is not a good architecture to loop a sql statement, but this the only way I made it work. Any ideas how I can make this without looping? Maybe a Linq statement, I never used Linq with this magnitude. Thank you.
protected void GetIds(string Type, string Sub)
{
LinkedIds.Clear();
using (SqlConnection cs = new SqlConnection(connstr))
{
for (int x = 0; x < ListTypes.Count; x++)
{
cs.Open();
SqlCommand select = new SqlCommand("spUI_LinkedIds", cs);
select.CommandType = System.Data.CommandType.StoredProcedure;
select.Parameters.AddWithValue("#Type", Type);
select.Parameters.AddWithValue("#Sub", Sub);
select.Parameters.AddWithValue("#TransId", ListTypes[x]);
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
ListIds.Add(Convert.ToInt32(dr["LinkedId"]));
}
cs.Close();
}
}
}
Not a full answer, but this wouldn't fit in a comment. You can at least update your existing code to be more efficient like this:
protected List<int> GetIds(string Type, string Sub, IEnumerable<int> types)
{
var result = new List<int>();
using (SqlConnection cs = new SqlConnection(connstr))
using (SqlCommand select = new SqlCommand("spUI_LinkedIds", cs))
{
select.CommandType = System.Data.CommandType.StoredProcedure;
//Don't use AddWithValue! Be explicit about your DB types
// I had to guess here. Replace with the actual types from your database
select.Parameters.Add("#Type", SqlDBType.VarChar, 10).Value = Type;
select.Parameters.Add("#Sub", SqlDbType.VarChar, 10).Value = Sub;
var TransID = select.Parameters.Add("#TransId", SqlDbType.Int);
cs.Open();
foreach(int type in types)
{
TransID.Value = type;
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
result.Add((int)dr["LinkedId"]);
}
}
}
return result;
}
Note that this way you only open and close the connection once. Normally in ADO.Net it's better to use a new connection and re-open it for each query. The exception is in a tight loop like this. Also, the only thing that changes inside the loop this way is the one parameter value. Finally, it's better to design methods that don't rely on other class state. This method no longer needs to know about the ListTypes and ListIds class variables, which makes it possible to (among other things) do better unit testing on the method.
Again, this isn't a full answer; it's just an incremental improvement. What you really need to do is write another stored procedure that accepts a table valued parameter, and build on the query from your existing stored procedure to JOIN with the table valued parameter, so that all of this will fit into a single SQL statement. But until you share your stored procedure code, this is about as much help as I can give you.
Besides the improvements others wrote.
You could insert your ID's into a temp table and then make one
SELECT * from WhatEverTable WHERE transid in (select transid from #tempTable)
On a MSSQL this works really fast.
When you're not using a MSSQL it could be possible that one great SQL-Select with joins is faster than a SELECT IN. You have to test these cases by your own on your DBMS.
According to your comment:
The idea is lets say I have a table and I have to get all records from the table that has this 10 types of products. How can I get all of this products? But this number is dynamic.
So... why use a stored procedure at all? Why not query the table?
//If [Type] and [Sub] arguments are external inputs - as in, they come from a user request or something - they should be sanitized. (remove or escape '\' and apostrophe signs)
//create connection
string queryTmpl = "SELECT LinkedId FROM [yourTable] WHERE [TYPE] = '{0}' AND [SUB] = '{1}' AND [TRANSID] IN ({2})";
string query = string.Format(queryTmpl, Type, Sub, string.Join(", ", ListTypes);
SqlCommand select = new SqlCommand(query, cs);
//and so forth
To use Linq-to-SQL you would need to map the table to a class. This would make the query simpler to perform.
I'm trying to display the number of records in the data reader. Here's what I tried.
if (mybtnreader1.HasRows)
{
using (DataTable dt = new DataTable())
{
dt.Load(mybtnreader1);
int rc = dt.Rows.Count;
MessageBox.Show("Have "+rc+"records");
}
}
Though it has records it is always displaying 0. How should it be corrected or is there any other way to get the number of records in a data reader?
I'm using this code to display the data.
while(mybtnreader1.Read())
{
MessageBox.Show(mybtnreader1.GetValue(0) + " "+mybtnreader1.GetValue(1)+" ");
}
It is showing the data but when it comes to the number of records it is displaying 0.
After looping through the results of your query you can use RecordsAffected:
mybtnreader1 = command.ExecuteReader();
while(mybtnreader1.Read())
{
///do your stuff
}
mybtnreader1 .Close();
MessageBox.Show(mybtnreader1 .RecordsAffected.ToString());
A DataReader is forward-only read-only so you can't get the number of records before looping through them all. While you loop through you can count, but not before.
If you need to know the number of records ahead of time and want the performance and memory advantages of a DataReader, then change your query to run two queries.. first the same underlying query with a select count(*)... and then the actual query. Depending on the query, this will obviously affect performance. It won't be double the time due to caching, but is additional processing time. You'll have to weigh the need for having the count ahead of time vs the advantages of using a DataReader vs a DataTable.
For example, if you're querying every record from a table like this:
string sql = "SELECT * FROM MyTable";
using(var dataReader = ...)
Then you can do this instead:
string sql = #"
SELECT COUNT(*) FROM MyTable;
SELECT * FROM MyTable;
};
using(var dataReader = ...)
{
... process first result in data reader (count) ...
if (dataReader.NextResult)
{
... process the second result (records) ...
}
}
You cannot do it directly with Datareader. you can do it like below -
SqlDataReader reader = command.ExecuteReader();
DataTable dt_results = new DataTable();
dt_results.Load(reader);
int count= dt_results.Rows.Count;
if(count>0)
{
//Hey! we Have records for this query
}
else{
//Sorry! No Records Exist for this query
}
basically what I want to do is read data from the table and then add that data to the appropriate list.
For example
List<int> value;
SELECT Values_To_Add FROM table
value.add(Values_To_Add)
Obviously using the correct C# MySql syntax. How should I go about doing this?
I think something like the below might what you are looking for:
List<int> values = new List<int>();
string sql = "SELECT Values_To_Add FROM table";
command.CommandText = sql;
MySqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
values.Add(reader["Values_To_Add "]);
}
You might what to Google about setting up MySqlReader etc.
Hope this helps, it should be a start.
try
List<int> values;
var result = from value in table
select value;
foreach(var item in result) values.Add(item);
Look into using the SQLDataReader Class.
After researching you should be able to use the data reader to read in rows from your table and subsequently add them to your list :)
Tutorial: http://csharp-station.com/Tutorial/AdoDotNet/Lesson04
MSDN: http://msdn.microsoft.com/en-GB/library/system.data.sqlclient.sqldatareader.aspx
It has been a long time since I have used .NET, but thankfully have almost finished writing a tool to compare an sqlite and mysql database. I am running into an issue though when trying to write a function for my wrapper that will handle SELECT calls as I cannot entirely figure out the Data Reader.
My understanding is that each iteration of a loop on the reader is the next row, and GetString(x) returns the column value for "x" as the index. All the examples I found though went into it knowing the row/column names they needed. How can I do this with a "SELECT * FROM" call and save the column names/values for later access? Microsoft seems to have a "FieldCount" function but I am coming up empty on the MySQL Connector.
Thanks!
public void Query(string query, string tableName)
{
//Open connection
if (this.OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the list
while (dataReader.Read())
{
int count = Count(tableName);
for (int x = 0; x < count; x++)
{
Console.WriteLine(dataReader.GetString(count));
}
}
//close Data Reader
dataReader.Close();
//close Connection
this.CloseConnection();
}
}
You can use DbDataReader.GetName to get the name of a column given its ordinal x.
use the "mysql connector" to access data in MySql it is more simple then to write the queries by your self:
http://dev.mysql.com/downloads/connector/net/
Then use the EntityFramework to access the data through this connector. Also you can automaticly generate *.edmx model from your existing DB in mysql, this will let you to fastly access and work with the Data in your database. Here is information about adding *.edmx model from existing DB:
http://msdn.microsoft.com/en-us/library/vstudio/cc716703(v=vs.100).aspx
The query which will select all data from the table, for example - "Products" will look like that:
List products = dbContext.Products.Where(e=>e.ProductId!=-1).ToList();
this will return the whole list of products in your data base in table Products.
then you can work with products as you want. for example taking the "Name" column for the first product in 'products' will look like that:
String firstProductName = products[0].name;
I hope it helps.
I have a method for adding values to the database for all operations.
If this is selected from the database and this select return more rows from the database,
how can I get the rows and store in an array?
This is the method code :
public void ExcuteProcedure(string procName, List<SqlParameter> procparams)
{
try
{
SqlConnection mycon = new SqlConnection(connectionString);
mycon.Open();
SqlCommand mycom = new SqlCommand();
mycom.Connection = mycon;
mycom.CommandText = procName;
mycom.CommandType = CommandType.StoredProcedure;
foreach (var item in procparams)
{
SqlParameter myparm = new SqlParameter();
myparm.ParameterName = item.ParameterName;
// myparm.SqlDbType = item.SqlDbType;
myparm.Value = item.Value;
mycom.Parameters.Add(myparm);
}
var n= mycom.ExecuteScalar();
mycon.Close();
}
catch (SqlException e)
{
Console.WriteLine("Error Number is : " + e.Number);
Console.WriteLine("Error Message is : " + e.Message);
}
}
You need to call mycom.ExecuteReader(), which will give you a SqlDataReader which can read through the results.
Call Read() to advance through the rows.
It never ceases to amaze me the number of times I see devs trying to abstract away simple database connectivity; and the myriad of ways they inevitably screw it up.
The following may sound mean, but it needs said:
Clean up your code, it leaks like a sieve. Using clauses around the connection and command objects are pretty much mandatory. As it stands if you forget a single parameter or put in a bad value you will leak connections. Once the connection pool is filled up your app will crash in all sorts of interesting, and usually hard to debug, ways.
Next, if you aren't sure how to properly get records back from a database then you probably shouldn't try to abstract the code calling your procedures. Either use a lightweight ORM like Dapper or learn how what you are doing will ultimately involve a lot of extraneous code that the next developer on your project will want to rip out.
/rant over.
Getting back to the question: ExecuteScalar returns a single value. You need to use ExecuteReader. I'd suggest that you simply take the results of the reader, stuff it into a datatable and pass that back to the calling code.
var n = mycom.ExecuteScalar();
Scalar: an atomic quantity that can hold only one value at a time
Return a DataReader instead, and iterate through its rows
Fill a DataSet by using a DataAdapter (this is more appropriate if you have multiple tables in the result set).